// import com.alibaba.druid.DbType_ptr;
// import com.alibaba.druid.sql.SQLUtils;
// import com.alibaba.druid.sql.ast.*;
// import com.alibaba.druid.sql.ast.expr.*;
// import com.alibaba.druid.sql.ast.statement.*;
// import com.alibaba.druid.sql.dialect.hive.ast.HiveInsert;
// import com.alibaba.druid.sql.dialect.hive.ast.HiveMultiInsertStatement;
// import com.alibaba.druid.sql.dialect.hive.stmt.HiveCreateTableStatement;
// import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlExpr;
// import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter;
// import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleExpr;
// import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitorAdapter;
// import com.alibaba.druid.sql.dialect.postgresql.visitor.PGASTVisitorAdapter;
// import com.alibaba.druid.sql.repository.SchemaObject;
// import com.alibaba.druid.sql.repository.SchemaRepository_ptr;
// import com.alibaba.druid.stat.TableStat_ptr;
// import com.alibaba.druid.stat.TableStat_ptr.TableStat_Column_ptr;
// import com.alibaba.druid.stat.TableStat_ptr.Condition;
// import com.alibaba.druid.stat.TableStat_ptr.TableStat_Mode_ptr;
// import com.alibaba.druid.stat.TableStat_ptr.Relationship;
// import com.alibaba.druid.util.FnvHash;

// import java.util.*;

#include "SchemaStatVisitor.h"
#include "SQLEvalVisitorUtils.h"
#include "SQLEvalVisitor.h"
#include "../ast/statement/SQLExprTableSource.h"
#include "../ast/statement/SQLSubqueryTableSource.h"
#include "../ast/statement/SQLSelectOrderByItem.h"
#include "../ast/statement/SQLTruncateStatement.h"
#include "../ast/statement/SQLDropViewStatement.h"
#include "../ast/statement/SQLDropTableStatement.h"
#include "../ast/statement/SQLAlterTableMergePartition.h"
#include "../ast/statement/SQLAlterTableRenamePartition.h"
#include "../ast/statement/SQLAlterTableAddIndex.h"
#include "../ast/statement/SQLInsertStatement.h"
#include "../ast/statement/SQLJoinTableSource.h"
#include "../ast/statement/SQLSelectQuery.h"
#include "../ast/statement/SQLColumnDefinition.h"
#include "../ast/statement/SQLSelectStatement.h"
#include "../ast/statement/SQLWithSubqueryClause.h"
#include "../ast/statement/SQLCreateTableStatement.h"
#include "../ast/statement/SQLDescribeStatement.h"
#include "../ast/statement/SQLRevokeStatement.h"
#include "../ast/statement/SQLUpdateStatement.h"
#include "../ast/statement/SQLUpdateSetItem.h"
#include "../ast/statement/SQLDeleteStatement.h"
#include "../ast/statement/SQLAlterTableAddColumn.h"
#include "../ast/statement/SQLCreateFunctionStatement.h"
#include "../ast/statement/SQLAnalyzeTableStatement.h"
#include "../ast/statement/SQLShowCreateTableStatement.h"
#include "../ast/statement/SQLAlterTableAddPartition.h"
#include "../ast/statement/SQLPartitionRef.h"
#include "../ast/statement/SQLUnique.h"
#include "../ast/statement/SQLShowColumnsStatement.h"
#include "../ast/statement/SQLExprStatement.h"
#include "../ast/statement/SQLReplaceStatement.h"
#include "../ast/statement/SQLAlterTableStatement.h"
#include "../ast/statement/SQLCreateViewStatement.h"
#include "../ast/statement/SQLCreateIndexStatement.h"
#include "../ast/statement/SQLAlterViewStatement.h"
#include "../ast/statement/SQLBlockStatement.h"
#include "../ast/statement/SQLDropIndexStatement.h"
#include "../ast/statement/SQLCreateTriggerStatement.h"
#include "../ast/statement/SQLCreateProcedureStatement.h"
#include "../ast/statement/SQLCloneTableStatement.h"
#include "../ast/statement/SQLAlterTableAddConstraint.h"
#include "../ast/statement/SQLExplainStatement.h"
#include "../ast/statement/SQLAlterTableDropIndex.h"
#include "../ast/statement/SQLAlterTableExchangePartition.h"
#include "../ast/statement/SQLPrimaryKey.h"
#include "../ast/statement/SQLCreateMaterializedViewStatement.h"
#include "../ast/statement/SQLLateralViewTableSource.h"
#include "../ast/statement/SQLDumpStatement.h"
#include "../ast/statement/SQLImportTableStatement.h"
#include "../ast/statement/SQLShowIndexesStatement.h"
#include "../ast/statement/SQLUnionQuery.h"
#include "../ast/statement/SQLObjectType.h"
#include "../ast/statement/SQLDefault.h"
#include "../ast/statement/SQLCheck.h"
#include "../ast/statement/SQLGrantStatement.h"
#include "../ast/statement/SQLAlterIndexStatement.h"
#include "../ast/statement/SQLExportTableStatement.h"
#include "../ast/statement/SQLCreateOutlineStatement.h"
#include "../ast/statement/SQLShowPartitionsStmt.h"
#include "../ast/statement/SQLAlterTableDropKey.h"
#include "../ast/statement/SQLMergeStatement.h"
#include "../ast/statement/SQLForeignKeyImpl.h"
#include "../ast/statement/SQLValuesTableSource.h"
#include "../ast/statement/SQLIfStatement.h"
#include "../ast/statement/SQLCopyFromStatement.h"
#include "../ast/statement/SQLAlterTableDropPrimaryKey.h"
#include "../ast/statement/SQLUnionQueryTableSource.h"
#include "../ast/statement/SQLWithSubqueryClause_Entry.h"
#include "../ast/expr/SQLMethodInvokeExpr.h"
#include "../ast/expr/SQLBinaryOpExpr.h"
#include "../ast/expr/SQLBetweenExpr.h"
#include "../ast/expr/SQLDbLinkExpr.h"
#include "../ast/expr/SQLBinaryOperator.h"
#include "../ast/expr/SQLQueryExpr.h"
#include "../ast/expr/SQLCastExpr.h"
#include "../ast/expr/SQLExprUtils.h"
#include "../ast/expr/SQLAllColumnExpr.h"
#include "../ast/expr/SQLAggregateExpr.h"
#include "../ast/expr/SQLInListExpr.h"
#include "../ast/expr/SQLIdentifierExpr.h"
#include "../ast/expr/SQLArrayExpr.h"
#include "../ast/expr/SQLInSubQueryExpr.h"
#include "../ast/SQLOver.h"
#include "../ast/SQLParameter.h"
#include "../ast/SQLCurrentTimeExpr.h"
#include "../ast/SQLDeclareItem.h"
#include "../ast/SQLOrderingSpecification.h"
#include "../../utils/instanceof.h"
#include "../../utils/StringUtils.h"
#include "../../utils/listTran.h"
#include "../../utils/ListUtils.h"
#include "../../utils/BOOL.h"
#include "../repository/SchemaRepository.h"
#include "../dialect/mysql/ast/expr/MySqlExpr.h"
#include "../dialect/oracle/ast/expr/OracleExpr.h"
#include "../dialect/hive/ast/HiveInsert.h"
#include "../dialect/hive/ast/HiveMultiInsertStatement.h"
#include "../repository/SchemaObject.h"

SchemaStatVisitor_OrderByStatVisitor::SchemaStatVisitor_OrderByStatVisitor(SQLOrderBy_ptr orderBy)
{
  this->orderBy = orderBy;
  for (SQLSelectOrderByItem_ptr item : *orderBy->getItems())
  {
    item->getExpr()->setParent(item);
  }
}

SchemaStatVisitor_MySqlOrderByStatVisitor::SchemaStatVisitor_MySqlOrderByStatVisitor(SQLOrderBy_ptr orderBy)
{
  this->orderBy = orderBy;
  for (SQLSelectOrderByItem_ptr item : *orderBy->getItems())
  {
    item->getExpr()->setParent(item);
  }
}

SchemaStatVisitor_PGOrderByStatVisitor::SchemaStatVisitor_PGOrderByStatVisitor(SQLOrderBy_ptr orderBy)
{
  this->orderBy = orderBy;
  for (SQLSelectOrderByItem_ptr item : *orderBy->getItems())
  {
    item->getExpr()->setParent(item);
  }
}

SchemaStatVisitor_OracleOrderByStatVisitor::SchemaStatVisitor_OracleOrderByStatVisitor(SQLOrderBy_ptr orderBy)
{
  this->orderBy = orderBy;
  for (SQLSelectOrderByItem_ptr item : *orderBy->getItems())
  {
    item->getExpr()->setParent(item);
  }
}

bool SchemaStatVisitor_OracleOrderByStatVisitor::visit(SQLPropertyExpr_ptr x)
{
  SQLExpr_ptr unwrapped = unwrapExpr(x);
  if (instanceof <SQLExpr, SQLPropertyExpr>(unwrapped))
  {
    visitOrderBy(std::dynamic_pointer_cast<SQLPropertyExpr>(unwrapped));
  }
  else if (instanceof <SQLExpr, SQLIdentifierExpr>(unwrapped))
  {
    visitOrderBy(std::dynamic_pointer_cast<SQLIdentifierExpr>(unwrapped));
  }
  return false;
}

SchemaStatVisitor::SchemaStatVisitor()
    : SchemaStatVisitor((DbType_ptr) nullptr)
{
}

SchemaStatVisitor::SchemaStatVisitor(SchemaRepository_ptr repository)
{
  if (repository != nullptr)
  {
    this->dbType = repository->getDbType();
  }
  this->repository = repository;

  SQLName_list_ptr originalTables = std::make_shared<std::list<SQLName_ptr>>();
  // std::shared_ptr<std::map<TableStat_Name, TableStat_ptr>> tableStats = std::make_shared<std::map<TableStat_Name, TableStat_ptr>>();
  std::shared_ptr<std::map<std::string, TableStat_ptr>> tableStats = std::make_shared<std::map<std::string, TableStat_ptr>>();
  // std::map<long, TableStat_Column_ptr>  columns = new LinkedHashMap<Long, TableStat_Column_ptr>();
  TableStat_Condition_list_ptr conditions = std::make_shared<std::list<TableStat_Condition_ptr>>();
  std::shared_ptr<std::set<TableStat_Relationship_ptr>> relationships = std::make_shared<std::set<TableStat_Relationship_ptr>>();
  TableStat_Column_list_ptr orderByColumns = std::make_shared<std::list<TableStat_Column_ptr>>();
  std::shared_ptr<std::set<TableStat_Column_ptr>> groupByColumns = std::make_shared<std::set<TableStat_Column_ptr>>();
  SQLAggregateExpr_list_ptr aggregateFunctions = std::make_shared<std::list<SQLAggregateExpr_ptr>>();
  SQLMethodInvokeExpr_list_ptr functions = std::make_shared<std::list<SQLMethodInvokeExpr_ptr>>();
  Object_list_ptr parameters = std::make_shared<std::list<Object_ptr>>();
}

SchemaStatVisitor::SchemaStatVisitor(DbType_ptr dbType)
    : SchemaStatVisitor(SchemaRepository_ptr(new SchemaRepository(dbType)), std::make_shared<std::list<Object_ptr>>())
{

  this->dbType = dbType;
}

SchemaStatVisitor::SchemaStatVisitor(Object_list_ptr parameters)
    : SchemaStatVisitor((DbType_ptr) nullptr, parameters)
{
}

SchemaStatVisitor::SchemaStatVisitor(DbType_ptr dbType, Object_list_ptr parameters)
    : SchemaStatVisitor(SchemaRepository_ptr(new SchemaRepository(dbType)), parameters)
{

  this->parameters = parameters;
}

SchemaStatVisitor::SchemaStatVisitor(SchemaRepository_ptr repository, Object_list_ptr parameters)
{
  this->repository = repository;
  this->parameters = parameters;
  if (repository != nullptr)
  {
    DbType_ptr dbType = repository->getDbType();
    if (dbType != nullptr && this->dbType == nullptr)
    {
      this->dbType = dbType;
    }
  }
  SQLName_list_ptr originalTables = std::make_shared<std::list<SQLName_ptr>>();
  // std::shared_ptr<std::map<TableStat_Name, TableStat_ptr>> tableStats = std::make_shared<std::map<TableStat_Name, TableStat_ptr>>();
  std::shared_ptr<std::map<std::string, TableStat_ptr>> tableStats = std::make_shared<std::map<std::string, TableStat_ptr>>();
  // std::map<long, TableStat_Column_ptr>  columns = new LinkedHashMap<Long, TableStat_Column_ptr>();
  TableStat_Condition_list_ptr conditions = std::make_shared<std::list<TableStat_Condition_ptr>>();
  std::shared_ptr<std::set<TableStat_Relationship_ptr>> relationships = std::make_shared<std::set<TableStat_Relationship_ptr>>();
  TableStat_Column_list_ptr orderByColumns = std::make_shared<std::list<TableStat_Column_ptr>>();
  std::shared_ptr<std::set<TableStat_Column_ptr>> groupByColumns = std::make_shared<std::set<TableStat_Column_ptr>>();
  SQLAggregateExpr_list_ptr aggregateFunctions = std::make_shared<std::list<SQLAggregateExpr_ptr>>();
  SQLMethodInvokeExpr_list_ptr functions = std::make_shared<std::list<SQLMethodInvokeExpr_ptr>>();
}

SchemaRepository_ptr SchemaStatVisitor::getRepository()
{
  return repository;
}

void SchemaStatVisitor::setRepository(SchemaRepository_ptr repository)
{
  this->repository = repository;
}

Object_list_ptr SchemaStatVisitor::getParameters()
{
  return parameters;
}

void SchemaStatVisitor::setParameters(Object_list_ptr parameters)
{
  this->parameters = parameters;
}

TableStat_ptr SchemaStatVisitor::getTableStat(string_ptr tableName)
{
  tableName = handleName(tableName);

  // TableStat_Name tableNameObj = TableStat_Name(tableName);
  std::string tableNameObj = tableName->c_str();
  auto stat = tableStats->find(tableNameObj);
  if (stat == tableStats->end())
  {
    TableStat_ptr statPtr = TableStat_ptr(new TableStat());
    // tableStats->insert(std::make_pair(TableStat_Name(tableName), statPtr));
    tableStats->insert(std::make_pair(tableNameObj, statPtr));
    return statPtr;
  }
  return stat->second;
}

TableStat_ptr SchemaStatVisitor::getTableStat(SQLName_ptr tableName)
{
  string_ptr strName;
  if (instanceof <SQLName, SQLIdentifierExpr>(tableName))
  {
    strName = (std::dynamic_pointer_cast<SQLIdentifierExpr>(tableName))->normalizedName();
  }
  else if (instanceof <SQLName, SQLPropertyExpr>(tableName))
  {
    strName = (std::dynamic_pointer_cast<SQLPropertyExpr>(tableName))->normalizedName();
  }
  else
  {
    strName = tableName->toString();
  }

  long hashCode64 = tableName->hashCode64();

  if (hashCode64 == FnvHash::Constants::DUAL)
  {
    return nullptr;
  }

  originalTables->push_back(tableName);

  // TableStat_Name tableNameObj = TableStat_Name(strName, hashCode64);
  std::string tableNameObj = strName->c_str();
  auto stat = tableStats->find(tableNameObj);
  if (stat == tableStats->end())
  {
    TableStat_ptr statPtr = TableStat_ptr(new TableStat());
    // tableStats->insert(std::make_pair(TableStat_Name(strName, hashCode64), statPtr));
    tableStats->insert(std::make_pair(tableNameObj, statPtr));
  }
  return stat->second;
}

TableStat_Column_ptr SchemaStatVisitor::addColumn(string_ptr tableName, string_ptr columnName)
{
  TableStat_Column_ptr c = TableStat_Column_ptr(new TableStat_Column(tableName, columnName, dbType));

  auto column = this->columns.find(c->hashCode64());
  if (column == this->columns.end() && columnName != nullptr)
  {
    // column = c;
    columns.insert(std::make_pair(c->hashCode64(), c));
    return c;
  }
  return column->second;
}

TableStat_Column_ptr SchemaStatVisitor::addColumn(SQLName_ptr table, string_ptr columnName)
{
  string_ptr tableName;
  if (instanceof <SQLName, SQLIdentifierExpr>(table))
  {
    tableName = (std::dynamic_pointer_cast<SQLIdentifierExpr>(table))->normalizedName();
  }
  else if (instanceof <SQLName, SQLPropertyExpr>(table))
  {
    tableName = (std::dynamic_pointer_cast<SQLPropertyExpr>(table))->normalizedName();
  }
  else
  {
    tableName = table->toString();
  }

  long tableHashCode64 = table->hashCode64();

  long basic = tableHashCode64;
  basic ^= '.';
  basic *= FnvHash::PRIME;
  long columnHashCode64 = FnvHash::hashCode64(basic, columnName);

  auto column = this->columns.find(columnHashCode64);
  if (column == this->columns.end() && columnName != nullptr)
  {
    TableStat_Column_ptr columnPtr = TableStat_Column_ptr(new TableStat_Column(tableName, columnName, columnHashCode64));
    columns.insert(std::make_pair(columnHashCode64, columnPtr));
  }
  return column->second;
}

string_ptr SchemaStatVisitor::handleName(string_ptr ident)
{
  int len = ident->length();
  if (ident->at(0) == '[' && ident->at(len - 1) == ']')
  {
    ident = make_string_ptr(ident->substr(1, len - 1));
  }
  else
  {
    bool flag0 = false;
    bool flag1 = false;
    bool flag2 = false;
    bool flag3 = false;
    for (int i = 0; i < len; ++i)
    {
      char ch = ident->at(i);
      if (ch == '\"')
      {
        flag0 = true;
      }
      else if (ch == '`')
      {
        flag1 = true;
      }
      else if (ch == ' ')
      {
        flag2 = true;
      }
      else if (ch == '\'')
      {
        flag3 = true;
      }
    }
    if (flag0)
    {
      ident = StringUtils::replaceAll(ident, make_string_ptr("\""), make_string_ptr(""));
    }

    if (flag1)
    {
      ident = StringUtils::replaceAll(ident, make_string_ptr("`"), make_string_ptr(""));
    }

    if (flag2)
    {
      ident = StringUtils::replaceAll(ident, make_string_ptr(" "), make_string_ptr(""));
    }

    if (flag3)
    {
      ident = StringUtils::replaceAll(ident, make_string_ptr("'"), make_string_ptr(""));
    }
  }
  return ident;
}

void SchemaStatVisitor::setModeOrigin(SQLObject_ptr x)
{
  TableStat_Mode_ptr originalMode = std::dynamic_pointer_cast<TableStat_Mode>(x->getAttribute("_original_use_mode"));
  mode = originalMode;
}

TableStat_Mode_ptr SchemaStatVisitor::setMode(SQLObject_ptr x, TableStat_Mode_ptr mode)
{
  TableStat_Mode_ptr oldMode = this->mode;
  x->putAttribute("_original_use_mode", oldMode);
  this->mode = mode;
  return oldMode;
}

bool SchemaStatVisitor::visitOrderBy(SQLIdentifierExpr_ptr x)
{
  SQLTableSource_ptr tableSource = x->getResolvedTableSource();

  if (tableSource == nullptr && instanceof <SQLObject, SQLSelectOrderByItem>(x->getParent()) && instanceof <SQLObject, SQLOrderBy>(x->getParent()->getParent()) && instanceof <SQLObject, SQLSelectQueryBlock>(x->getParent()->getParent()->getParent()))
  {
    SQLSelectQueryBlock_ptr queryBlock = std::dynamic_pointer_cast<SQLSelectQueryBlock>(x->getParent()->getParent()->getParent());
    SQLSelectItem_ptr selectItem = queryBlock->findSelectItem(x->nameHashCode64());
    if (selectItem != nullptr)
    {
      SQLExpr_ptr selectItemExpr = selectItem->getExpr();
      if (instanceof <SQLExpr, SQLIdentifierExpr>(selectItemExpr))
      {
        x = std::dynamic_pointer_cast<SQLIdentifierExpr>(selectItemExpr);
      }
      else if (instanceof <SQLExpr, SQLPropertyExpr>(selectItemExpr))
      {
        return visitOrderBy(std::dynamic_pointer_cast<SQLPropertyExpr>(selectItemExpr));
      }
      else
      {
        return false;
      }
    }
  }

  string_ptr tableName = nullptr;
  if (instanceof <SQLTableSource, SQLExprTableSource>(tableSource))
  {
    SQLExpr_ptr expr = (std::dynamic_pointer_cast<SQLExprTableSource>(tableSource))->getExpr();
    if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
    {
      SQLIdentifierExpr_ptr table = std::dynamic_pointer_cast<SQLIdentifierExpr>(expr);
      tableName = table->getName();
    }
    else if (instanceof <SQLExpr, SQLPropertyExpr>(expr))
    {
      SQLPropertyExpr_ptr table = std::dynamic_pointer_cast<SQLPropertyExpr>(expr);
      tableName = table->toString();
    }
    else if (instanceof <SQLExpr, SQLMethodInvokeExpr>(expr))
    {
      SQLMethodInvokeExpr_ptr methodInvokeExpr = std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr);
      if (StringUtils::equalsIgnoreCase(make_string_ptr("table"), methodInvokeExpr->getMethodName()) && methodInvokeExpr->getArguments()->size() == 1)
      {
        auto it_getArguments = methodInvokeExpr->getArguments()->begin();
        if (instanceof <SQLExpr, SQLName>(*it_getArguments))
        {
          SQLName_ptr table = std::dynamic_pointer_cast<SQLName>(*it_getArguments);

          if (instanceof <SQLName, SQLPropertyExpr>(table))
          {
            SQLPropertyExpr_ptr propertyExpr = std::dynamic_pointer_cast<SQLPropertyExpr>(table);
            SQLIdentifierExpr_ptr owner = std::dynamic_pointer_cast<SQLIdentifierExpr>(propertyExpr->getOwner());
            if (propertyExpr->getResolvedTableSource() != nullptr && instanceof <SQLTableSource, SQLExprTableSource>(propertyExpr->getResolvedTableSource()))
            {
              SQLExpr_ptr resolveExpr = (std::dynamic_pointer_cast<SQLExprTableSource>(propertyExpr->getResolvedTableSource()))->getExpr();
              if (instanceof <SQLExpr, SQLName>(resolveExpr))
              {
                std::string tmp = resolveExpr->toString()->c_str();
                tmp += ".";
                tmp += propertyExpr->getName()->c_str();
                tableName = make_string_ptr(tmp);
              }
            }
          }

          if (tableName == nullptr)
          {
            tableName = table->toString();
          }
        }
      }
    }
  }
  else if (instanceof <SQLTableSource, SQLWithSubqueryClause_Entry>(tableSource))
  {
    return false;
  }
  else if (instanceof <SQLTableSource, SQLSubqueryTableSource>(tableSource))
  {
    SQLSelectQueryBlock_ptr queryBlock = (std::dynamic_pointer_cast<SQLSubqueryTableSource>(tableSource))->getSelect()->getQueryBlock();
    if (queryBlock == nullptr)
    {
      return false;
    }

    SQLSelectItem_ptr selectItem = queryBlock->findSelectItem(x->nameHashCode64());
    if (selectItem == nullptr)
    {
      return false;
    }

    SQLExpr_ptr selectItemExpr = selectItem->getExpr();
    SQLTableSource_ptr columnTableSource = nullptr;
    if (instanceof <SQLExpr, SQLIdentifierExpr>(selectItemExpr))
    {
      columnTableSource = (std::dynamic_pointer_cast<SQLIdentifierExpr>(selectItemExpr))->getResolvedTableSource();
    }
    else if (instanceof <SQLExpr, SQLPropertyExpr>(selectItemExpr))
    {
      columnTableSource = (std::dynamic_pointer_cast<SQLPropertyExpr>(selectItemExpr))->getResolvedTableSource();
    }

    if (instanceof <SQLTableSource, SQLExprTableSource>(columnTableSource) && (instanceof <SQLExpr, SQLName>(std::dynamic_pointer_cast<SQLExprTableSource>(columnTableSource)->getExpr())))
    {
      SQLName_ptr tableExpr = std::dynamic_pointer_cast<SQLName>((std::dynamic_pointer_cast<SQLExprTableSource>(columnTableSource))->getExpr());
      if (instanceof <SQLName, SQLIdentifierExpr>(tableExpr))
      {
        tableName = (std::dynamic_pointer_cast<SQLIdentifierExpr>(tableExpr))->normalizedName();
      }
      else if (instanceof <SQLName, SQLPropertyExpr>(tableExpr))
      {
        tableName = (std::dynamic_pointer_cast<SQLPropertyExpr>(tableExpr))->normalizedName();
      }
    }
  }
  else
  {
    BOOL_ptr skip = BOOL::FALSE;
    for (SQLObject_ptr parent = x->getParent(); parent != nullptr; parent = parent->getParent())
    {
      if (instanceof <SQLObject, SQLSelectQueryBlock>(parent))
      {
        SQLTableSource_ptr from = (std::dynamic_pointer_cast<SQLSelectQueryBlock>(parent))->getFrom();

        if (instanceof <SQLTableSource, SQLValuesTableSource>(from))
        {
          skip = BOOL::TRUE;
          break;
        }
      }
      else if (instanceof <SQLObject, SQLSelectQuery>(parent))
      {
        break;
      }
    }
  }

  string_ptr identName = x->getName();
  if (tableName != nullptr)
  {
    orderByAddColumn(tableName, identName, x);
  }
  else
  {
    orderByAddColumn(make_string_ptr("UNKNOWN"), identName, x);
  }
  return false;
}

bool SchemaStatVisitor::visitOrderBy(SQLPropertyExpr_ptr x)
{
  if (isSubQueryOrParamOrVariant(x))
  {
    return false;
  }

  string_ptr owner = nullptr;

  SQLTableSource_ptr tableSource = x->getResolvedTableSource();
  if (instanceof <SQLTableSource, SQLExprTableSource>(tableSource))
  {
    SQLExpr_ptr tableSourceExpr = (std::dynamic_pointer_cast<SQLExprTableSource>(tableSource))->getExpr();
    if (instanceof <SQLExpr, SQLName>(tableSourceExpr))
    {
      owner = tableSourceExpr->toString();
    }
  }

  if (owner == nullptr && instanceof <SQLExpr, SQLIdentifierExpr>(x->getOwner()))
  {
    owner = (std::dynamic_pointer_cast<SQLIdentifierExpr>(x->getOwner()))->getName();
  }

  if (owner == nullptr)
  {
    return false;
  }

  orderByAddColumn(owner, x->getName(), x);

  return false;
}

bool SchemaStatVisitor::visitOrderBy(SQLIntegerExpr_ptr x)
{
  SQLObject_ptr parent = x->getParent();
  if (!(instanceof <SQLObject, SQLSelectOrderByItem>(parent)))
  {
    return false;
  }

  if (instanceof <SQLObject, SQLSelectQueryBlock>(parent->getParent()))
  {
    int selectItemIndex = x->getNumber() - 1;
    SQLSelectQueryBlock_ptr queryBlock = std::dynamic_pointer_cast<SQLSelectQueryBlock>(parent->getParent());
    SQLSelectItem_list_ptr selectList = queryBlock->getSelectList();

    if (selectItemIndex < 0 || selectItemIndex >= selectList->size())
    {
      return false;
    }

    auto it_selectList = selectList->begin();
    std::advance(it_selectList, selectItemIndex);
    SQLExpr_ptr selectItemExpr = (*it_selectList)->getExpr();
    if (instanceof <SQLExpr, SQLIdentifierExpr>(selectItemExpr))
    {
      visitOrderBy(std::dynamic_pointer_cast<SQLIdentifierExpr>(selectItemExpr));
    }
    else if (instanceof <SQLExpr, SQLPropertyExpr>(selectItemExpr))
    {
      visitOrderBy(std::dynamic_pointer_cast<SQLPropertyExpr>(selectItemExpr));
    }
  }

  return false;
}

void SchemaStatVisitor::orderByAddColumn(string_ptr table, string_ptr columnName, SQLObject_ptr expr)
{
  TableStat_Column_ptr column = TableStat_Column_ptr(new TableStat_Column(table, columnName, dbType));

  SQLObject_ptr parent = expr->getParent();
  if (instanceof <SQLObject, SQLSelectOrderByItem>(parent))
  {
    SQLOrderingSpecification_ptr type = (std::dynamic_pointer_cast<SQLSelectOrderByItem>(parent))->getType();
    column->getAttributes()->insert(std::make_pair("orderBy.type", type));
  }

  orderByColumns->push_back(column);
}

bool SchemaStatVisitor::visit(SQLOrderBy_ptr x)
{
  SQLASTVisitor_ptr orderByVisitor = createOrderByVisitor(x);

  SQLSelectQueryBlock_ptr query = nullptr;
  if (instanceof <SQLObject, SQLSelectQueryBlock>(x->getParent()))
  {
    query = std::dynamic_pointer_cast<SQLSelectQueryBlock>(x->getParent());
  }
  if (query != nullptr)
  {
    for (SQLSelectOrderByItem_ptr item : *x->getItems())
    {
      SQLExpr_ptr expr = item->getExpr();
      if (instanceof <SQLExpr, SQLIntegerExpr>(expr))
      {
        int intValue = (std::dynamic_pointer_cast<SQLIntegerExpr>(expr))->getNumber() - 1;
        if (intValue < query->getSelectList()->size())
        {
          auto it_getSelectList = query->getSelectList()->begin();
          std::advance(it_getSelectList, intValue);
          SQLExpr_ptr selectItemExpr = (*it_getSelectList)->getExpr();
          selectItemExpr->accept(orderByVisitor);
        }
      }
      else if (instanceof <SQLExpr, MySqlExpr>(expr) || instanceof <SQLExpr, OracleExpr>(expr))
      {
        continue;
      }
    }
  }
  x->accept(orderByVisitor);

  for (SQLSelectOrderByItem_ptr orderByItem : *x->getItems())
  {
    statExpr(orderByItem->getExpr());
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLOver_ptr x)
{
  SQLName_ptr of = x->getOf();
  SQLOrderBy_ptr orderBy = x->getOrderBy();
  SQLExpr_list_ptr partitionBy = x->getPartitionBy();

  if (of == nullptr // skip if of is not nullptr
      && orderBy != nullptr)
  {
    orderBy->accept(SharedObject(SchemaStatVisitor));
  }

  if (partitionBy != nullptr)
  {
    for (SQLExpr_ptr expr : *partitionBy)
    {
      expr->accept(SharedObject(SchemaStatVisitor));
    }
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLWindow_ptr x)
{
  SQLOver_ptr over = x->getOver();
  if (over != nullptr)
  {
    visit(over);
  }
  return false;
}

SQLASTVisitor_ptr SchemaStatVisitor::createOrderByVisitor(SQLOrderBy_ptr x)
{
  if (dbType == nullptr)
  {
    dbType = DbType_ptr(new DbType(&DbType::other));
  }

  SQLASTVisitor_ptr orderByVisitor;
  if (dbType->name->c_str() == DbType::mysql.name->c_str() ||
      dbType->name->c_str() == DbType::mariadb.name->c_str() ||
      dbType->name->c_str() == DbType::tidb.name->c_str())
  {
    return SchemaStatVisitor_MySqlOrderByStatVisitor_ptr(new SchemaStatVisitor_MySqlOrderByStatVisitor(x));
  }
  else if (dbType->name->c_str() == DbType::postgresql.name->c_str())
  {
    return SchemaStatVisitor_PGOrderByStatVisitor_ptr(new SchemaStatVisitor_PGOrderByStatVisitor(x));
  }
  else if (dbType->name->c_str() == DbType::oracle.name->c_str())
  {
    return SchemaStatVisitor_OracleOrderByStatVisitor_ptr(new SchemaStatVisitor_OracleOrderByStatVisitor(x));
  }
  else
  {
    return SchemaStatVisitor_OrderByStatVisitor_ptr(new SchemaStatVisitor_OrderByStatVisitor(x));
  }
}

bool SchemaStatVisitor::visit(SQLBetweenExpr_ptr x)
{
  SQLObject_ptr parent = x->getParent();

  SQLExpr_ptr test = x->getTestExpr();
  SQLExpr_ptr begin = x->getBeginExpr();
  SQLExpr_ptr end = x->getEndExpr();

  statExpr(test);
  statExpr(begin);
  statExpr(end);

  SQLExpr_list_ptr tmpList = std::make_shared<std::list<SQLExpr_ptr>>();
  tmpList->push_back(begin);
  tmpList->push_back(end);
  handleCondition(test, make_string_ptr("BETWEEN"), tmpList);

  return false;
}

bool SchemaStatVisitor::visit(SQLBinaryOpExpr_ptr x)
{
  SQLObject_ptr parent = x->getParent();

  if (instanceof <SQLObject, SQLIfStatement>(parent))
  {
    return true;
  }

  SQLBinaryOperator_ptr op = x->getOperator();
  SQLExpr_ptr left = x->getLeft();
  SQLExpr_ptr right = x->getRight();

  if ((op->name->c_str() == SQLBinaryOperator::BooleanAnd.name->c_str() ||
       op->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str()) &&
      instanceof <SQLExpr, SQLBinaryOpExpr>(left) && (std::dynamic_pointer_cast<SQLBinaryOpExpr>(left))->getOperator()->name->c_str() == op->name->c_str())
  {
    SQLExpr_list_ptr groupList = SQLBinaryOpExpr::split(x, op);
    auto it_groupList = groupList->begin();
    for (int i = 0; i < groupList->size(); i++)
    {
      std::advance(it_groupList, i);
      SQLExpr_ptr item = *it_groupList;
      item->accept(SharedObject(SchemaStatVisitor));
    }
    return false;
  }

  if (op->name->c_str() == SQLBinaryOperator::Equality.name->c_str() ||
      op->name->c_str() == SQLBinaryOperator::NotEqual.name->c_str() ||
      op->name->c_str() == SQLBinaryOperator::GreaterThan.name->c_str() ||
      op->name->c_str() == SQLBinaryOperator::GreaterThanOrEqual.name->c_str() ||
      op->name->c_str() == SQLBinaryOperator::LessThan.name->c_str() ||
      op->name->c_str() == SQLBinaryOperator::LessThanOrGreater.name->c_str() ||
      op->name->c_str() == SQLBinaryOperator::LessThanOrEqual.name->c_str() ||
      op->name->c_str() == SQLBinaryOperator::LessThanOrEqualOrGreaterThan.name->c_str() ||
      op->name->c_str() == SQLBinaryOperator::SoudsLike.name->c_str() ||
      op->name->c_str() == SQLBinaryOperator::Like.name->c_str() ||
      op->name->c_str() == SQLBinaryOperator::NotLike.name->c_str() ||
      op->name->c_str() == SQLBinaryOperator::Is.name->c_str() ||
      op->name->c_str() == SQLBinaryOperator::IsNot.name->c_str())
  {
    handleCondition(left, op->name, right);

    string_ptr reverseOp = op->name;

    if (op->name->c_str() == SQLBinaryOperator::LessThan.name->c_str())
    {
      reverseOp = SQLBinaryOperator::GreaterThan.name;
    }
    else if (op->name->c_str() == SQLBinaryOperator::LessThanOrEqual.name->c_str())
    {
      reverseOp = SQLBinaryOperator::GreaterThanOrEqual.name;
    }
    else if (op->name->c_str() == SQLBinaryOperator::GreaterThan.name->c_str())
    {
      reverseOp = SQLBinaryOperator::LessThan.name;
    }
    else if (op->name->c_str() == SQLBinaryOperator::GreaterThanOrEqual.name->c_str())
    {
      reverseOp = SQLBinaryOperator::LessThanOrEqual.name;
    }
    handleCondition(right, reverseOp, left);

    handleRelationship(left, op->name, right);
  }
  else if (op->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str())
  {
    SQLExpr_list_ptr list = SQLBinaryOpExpr::split(x, op);

    for (SQLExpr_ptr item : *list)
    {
      if (instanceof <SQLExpr, SQLBinaryOpExpr>(item))
      {
        visit(std::dynamic_pointer_cast<SQLBinaryOpExpr>(item));
      }
      else
      {
        item->accept(SharedObject(SchemaStatVisitor));
      }
    }

    return false;
  }
  else if (op->name->c_str() == SQLBinaryOperator::Modulus.name->c_str())
  {
    if (instanceof <SQLExpr, SQLIdentifierExpr>(right))
    {
      long hashCode64 = (std::dynamic_pointer_cast<SQLIdentifierExpr>(right))->hashCode64();
      if (hashCode64 == FnvHash::Constants::ISOPEN)
      {
        left->accept(SharedObject(SchemaStatVisitor));
        return false;
      }
    }
  }

  if (instanceof <SQLExpr, SQLBinaryOpExpr>(left))
  {
    visit(std::dynamic_pointer_cast<SQLBinaryOpExpr>(left));
  }
  else
  {
    statExpr(left);
  }
  statExpr(right);

  return false;
}

void SchemaStatVisitor::handleRelationship(SQLExpr_ptr left, string_ptr operator_, SQLExpr_ptr right)
{
  TableStat_Column_ptr leftColumn = getColumn(left);
  if (leftColumn == nullptr)
  {
    return;
  }

  TableStat_Column_ptr rightColumn = getColumn(right);
  if (rightColumn == nullptr)
  {
    return;
  }

  TableStat_Relationship_ptr relationship = TableStat_Relationship_ptr(new TableStat_Relationship(leftColumn, rightColumn, operator_));
  this->relationships->insert(relationship);
}

void SchemaStatVisitor::handleCondition(SQLExpr_ptr expr, string_ptr operator_, SQLExpr_ptr values)
{
  SQLExpr_list_ptr tmpList = std::make_shared<std::list<SQLExpr_ptr>>();
  tmpList->push_back(values);
  handleCondition(expr, operator_, tmpList);
}

void SchemaStatVisitor::handleCondition(SQLExpr_ptr expr, string_ptr operator_, SQLExpr_list_ptr valueExprs)
{
  if (instanceof <SQLExpr, SQLCastExpr>(expr))
  {
    expr = (std::dynamic_pointer_cast<SQLCastExpr>(expr))->getExpr();
  }
  else if (instanceof <SQLExpr, SQLMethodInvokeExpr>(expr))
  {
    SQLMethodInvokeExpr_ptr func = std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr);
    SQLExpr_list_ptr arguments = func->getArguments();
    if (func->methodNameHashCode64() == FnvHash::Constants::COALESCE && arguments->size() > 0)
    {
      bool allLiteral = true;
      auto it_arguments = arguments->begin();
      for (int i = 1; i < arguments->size(); ++i)
      {
        std::advance(it_arguments, i);
        SQLExpr_ptr arg = *it_arguments;
        if (!(instanceof <SQLExpr, SQLLiteralExpr>(arg)))
        {
          allLiteral = false;
        }
      }
      if (allLiteral)
      {
        std::advance(it_arguments, 0);
        expr = *it_arguments;
      }
    }
  }

  TableStat_Column_ptr column = getColumn(expr);

  if (column == nullptr && instanceof <SQLExpr, SQLBinaryOpExpr>(expr) && valueExprs->size() == 1)
  {
    auto it_valueExprs = valueExprs->begin();
    std::advance(it_valueExprs, 0);
    if (instanceof <SQLExpr, SQLLiteralExpr>(*it_valueExprs))
    {
      SQLBinaryOpExpr_ptr left = std::dynamic_pointer_cast<SQLBinaryOpExpr>(expr);
      SQLLiteralExpr_ptr right = std::dynamic_pointer_cast<SQLLiteralExpr>(*it_valueExprs);

      if (instanceof <SQLExpr, SQLIntegerExpr>(left->getRight()) &&
          instanceof <SQLLiteralExpr, SQLIntegerExpr>(right))
      {
        long v0 = (std::dynamic_pointer_cast<SQLIntegerExpr>(left->getRight()))->getNumber();
        long v1 = (std::dynamic_pointer_cast<SQLIntegerExpr>(right))->getNumber();

        SQLBinaryOperator_ptr op = left->getOperator();

        long v;
        if (op->name->c_str() == SQLBinaryOperator::Add.name->c_str())
        {
          v = v1 - v0;
        }
        else if (op->name->c_str() == SQLBinaryOperator::Subtract.name->c_str())
        {
          v = v1 + v0;
        }
        else
        {
          return;
        }

        handleCondition(left->getLeft(), operator_, SQLIntegerExpr_ptr(new SQLIntegerExpr(v)));
        return;
      }
    }
  }

  if (column == nullptr)
  {
    return;
  }

  TableStat_Condition_ptr condition = nullptr;
  for (TableStat_Condition_ptr item : *this->getConditions())
  {
    if (item->getColumn().get() == column.get() &&
        item->getOperator()->c_str() == operator_->c_str())
    {
      condition = item;
      break;
    }
  }

  if (condition == nullptr)
  {
    condition = TableStat_Condition_ptr(new TableStat_Condition(column, operator_));
    this->conditions->push_back(condition);
  }

  for (SQLExpr_ptr item : *valueExprs)
  {
    TableStat_Column_ptr valueColumn = getColumn(item);
    if (valueColumn != nullptr)
    {
      continue;
    }

    Object_ptr value;

    if (instanceof <SQLExpr, SQLCastExpr>(item))
    {
      item = (std::dynamic_pointer_cast<SQLCastExpr>(item))->getExpr();
    }

    if (instanceof <SQLExpr, SQLMethodInvokeExpr>(item) ||
        instanceof <SQLExpr, SQLCurrentTimeExpr>(item))
    {
      value = Object_ptr(new Object(item->toString()->c_str()));
    }
    else
    {
      value = SQLEvalVisitorUtils::eval(dbType, item, parameters, false);
      if (value == SQLEvalVisitor::EVAL_VALUE_NULL)
      {
        value = Object_ptr(new Object());
        value->setNull();
      }
    }

    condition->addValue(value);
  }
}

DbType_ptr SchemaStatVisitor::getDbType()
{
  return dbType;
}

TableStat_Column_ptr SchemaStatVisitor::getColumn(SQLExpr_ptr expr)
{
  SQLExpr_ptr original = expr;

  // unwrap
  expr = unwrapExpr(expr);

  if (instanceof <SQLExpr, SQLPropertyExpr>(expr))
  {
    SQLPropertyExpr_ptr propertyExpr = std::dynamic_pointer_cast<SQLPropertyExpr>(expr);

    SQLExpr_ptr owner = propertyExpr->getOwner();
    string_ptr column = SQLUtils::normalize(propertyExpr->getName());

    if (instanceof <SQLExpr, SQLName>(owner))
    {
      SQLName_ptr table = std::dynamic_pointer_cast<SQLName>(owner);

      SQLObject_ptr resolvedOwnerObject = propertyExpr->getResolvedOwnerObject();
      if (instanceof <SQLObject, SQLSubqueryTableSource>(resolvedOwnerObject) || instanceof <SQLObject, SQLCreateProcedureStatement>(resolvedOwnerObject) || instanceof <SQLObject, SQLCreateFunctionStatement>(resolvedOwnerObject))
      {
        table = nullptr;
      }

      if (instanceof <SQLObject, SQLExprTableSource>(resolvedOwnerObject))
      {
        SQLExpr_ptr tableSourceExpr = (std::dynamic_pointer_cast<SQLExprTableSource>(resolvedOwnerObject))->getExpr();
        if (instanceof <SQLExpr, SQLName>(tableSourceExpr))
        {
          table = std::dynamic_pointer_cast<SQLName>(tableSourceExpr);
        }
      }
      else if (instanceof <SQLObject, SQLValuesTableSource>(resolvedOwnerObject))
      {
        return nullptr;
      }

      if (table != nullptr)
      {
        string_ptr tableName;
        if (instanceof <SQLName, SQLIdentifierExpr>(table))
        {
          tableName = (std::dynamic_pointer_cast<SQLIdentifierExpr>(table))->normalizedName();
        }
        else if (instanceof <SQLName, SQLPropertyExpr>(table))
        {
          tableName = (std::dynamic_pointer_cast<SQLPropertyExpr>(table))->normalizedName();
        }
        else
        {
          tableName = table->toString();
        }

        long tableHashCode64 = table->hashCode64();

        if (instanceof <SQLObject, SQLExprTableSource>(resolvedOwnerObject))
        {
          SchemaObject_ptr schemaObject = (std::dynamic_pointer_cast<SQLExprTableSource>(resolvedOwnerObject))->getSchemaObject();
          if (schemaObject != nullptr && instanceof <SQLStatement, SQLCreateTableStatement>(schemaObject->getStatement()))
          {
            SQLColumnDefinition_ptr columnDef = schemaObject->findColumn(propertyExpr->nameHashCode64());
            if (columnDef == nullptr)
            {
              tableName = make_string_ptr("UNKNOWN");
              tableHashCode64 = FnvHash::Constants::UNKNOWN;
            }
          }
        }

        long basic = tableHashCode64;
        basic ^= '.';
        basic *= FnvHash::PRIME;
        long columnHashCode64 = FnvHash::hashCode64(basic, column);

        TableStat_Column_ptr columnObj = nullptr;
        auto it = this->columns.find(columnHashCode64);
        if (it != this->columns.end())
        {
          columnObj = it->second;
        }
        if (columnObj == nullptr)
        {
          columnObj = TableStat_Column_ptr(new TableStat_Column(tableName, column, columnHashCode64));
          if (!(instanceof <SQLObject, SQLSubqueryTableSource>(resolvedOwnerObject) || instanceof <SQLObject, SQLWithSubqueryClause_Entry>(resolvedOwnerObject)))
          {
            this->columns.insert(std::make_pair(columnHashCode64, columnObj));
          }
        }

        return columnObj;
      }
    }

    return nullptr;
  }

  if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
  {
    SQLIdentifierExpr_ptr identifierExpr = std::dynamic_pointer_cast<SQLIdentifierExpr>(expr);
    if (identifierExpr->getResolvedParameter() != nullptr)
    {
      return nullptr;
    }

    if (instanceof <SQLTableSource, SQLSubqueryTableSource>(identifierExpr->getResolvedTableSource()))
    {
      return nullptr;
    }

    if (identifierExpr->getResolvedDeclareItem() != nullptr || identifierExpr->getResolvedParameter() != nullptr)
    {
      return nullptr;
    }

    string_ptr column = identifierExpr->getName();

    SQLName_ptr table = nullptr;
    SQLTableSource_ptr tableSource = identifierExpr->getResolvedTableSource();
    if (instanceof <SQLTableSource, SQLExprTableSource>(tableSource))
    {
      SQLExpr_ptr tableSourceExpr = (std::dynamic_pointer_cast<SQLExprTableSource>(tableSource))->getExpr();

      if (tableSourceExpr != nullptr && !(instanceof <SQLExpr, SQLName>(tableSourceExpr)))
      {
        tableSourceExpr = unwrapExpr(tableSourceExpr);
      }

      if (instanceof <SQLExpr, SQLName>(tableSourceExpr))
      {
        table = std::dynamic_pointer_cast<SQLName>(tableSourceExpr);
      }
    }

    if (table != nullptr)
    {
      long tableHashCode64 = table->hashCode64();
      long basic = tableHashCode64;
      basic ^= '.';
      basic *= FnvHash::PRIME;
      long columnHashCode64 = FnvHash::hashCode64(basic, column);

      TableStat_Column_ptr old = nullptr;
      auto tmp = columns.find(columnHashCode64);
      if (tmp != columns.end())
      {
        old = tmp->second;
      }
      if (old != nullptr)
      {
        return old;
      }

      return TableStat_Column_ptr(new TableStat_Column(table->toString(), column, columnHashCode64));
    }

    return TableStat_Column_ptr(new TableStat_Column(make_string_ptr("UNKNOWN"), column));
  }

  if (instanceof <SQLExpr, SQLMethodInvokeExpr>(expr))
  {
    SQLMethodInvokeExpr_ptr methodInvokeExpr = std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr);
    SQLExpr_list_ptr arguments = methodInvokeExpr->getArguments();
    long nameHash = methodInvokeExpr->methodNameHashCode64();
    if (nameHash == FnvHash::Constants::DATE_FORMAT)
    {
      if (arguments->size() == 2)
      {
        auto it_arguments = arguments->begin();
        auto p0 = *it_arguments;
        std::advance(it_arguments, 1);
        auto p1 = *it_arguments;
        if (instanceof <SQLExpr, SQLName>(p0) && instanceof <SQLExpr, SQLCharExpr>(p1))
        {
          return getColumn(p0);
        }
      }
    }
  }

  return nullptr;
}

SQLExpr_ptr SchemaStatVisitor::unwrapExpr(SQLExpr_ptr expr)
{
  SQLExpr_ptr original = expr;

  for (int i = 0;; i++)
  {
    if (i > 1000)
    {
      return nullptr;
    }

    if (instanceof <SQLExpr, SQLMethodInvokeExpr>(expr))
    {
      SQLMethodInvokeExpr_ptr methodInvokeExp = std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr);
      if (methodInvokeExp->getArguments()->size() == 1)
      {
        SQLExpr_ptr firstExpr = (*(methodInvokeExp->getArguments()->begin()));
        expr = firstExpr;
        continue;
      }
    }

    if (instanceof <SQLExpr, SQLCastExpr>(expr))
    {
      expr = (std::dynamic_pointer_cast<SQLCastExpr>(expr))->getExpr();
      continue;
    }

    if (instanceof <SQLExpr, SQLPropertyExpr>(expr))
    {
      SQLPropertyExpr_ptr propertyExpr = std::dynamic_pointer_cast<SQLPropertyExpr>(expr);

      SQLTableSource_ptr resolvedTableSource = propertyExpr->getResolvedTableSource();
      if (instanceof <SQLTableSource, SQLSubqueryTableSource>(resolvedTableSource))
      {
        SQLSelect_ptr select = (std::dynamic_pointer_cast<SQLSubqueryTableSource>(resolvedTableSource))->getSelect();
        SQLSelectQueryBlock_ptr queryBlock = select->getFirstQueryBlock();
        if (queryBlock != nullptr)
        {
          if (queryBlock->getGroupBy() != nullptr)
          {
            if (instanceof <SQLObject, SQLBinaryOpExpr>(original->getParent()))
            {
              SQLExpr_ptr other = (std::dynamic_pointer_cast<SQLBinaryOpExpr>(original->getParent()))->other(original);
              if (!SQLExprUtils::isLiteralExpr(other))
              {
                break;
              }
            }
          }

          SQLSelectItem_ptr selectItem = queryBlock->findSelectItem(propertyExpr->nameHashCode64());
          if (selectItem != nullptr)
          {
            SQLExpr_ptr selectItemExpr = selectItem->getExpr();
            if (instanceof <SQLExpr, SQLMethodInvokeExpr>(selectItemExpr) && (std::dynamic_pointer_cast<SQLMethodInvokeExpr>(selectItemExpr))->getArguments()->size() == 1)
            {
              selectItemExpr = (*((std::dynamic_pointer_cast<SQLMethodInvokeExpr>(selectItemExpr))->getArguments()->begin()));
            }
            if (selectItemExpr != expr)
            {
              expr = selectItemExpr;
              continue;
            }
          }
          else if (queryBlock->selectItemHasAllColumn())
          {
            SQLTableSource_ptr allColumnTableSource = nullptr;

            SQLTableSource_ptr from = queryBlock->getFrom();
            if (instanceof <SQLTableSource, SQLJoinTableSource>(from))
            {
              SQLSelectItem_ptr allColumnSelectItem = queryBlock->findAllColumnSelectItem();
              if (allColumnSelectItem != nullptr && instanceof <SQLExpr, SQLPropertyExpr>(allColumnSelectItem->getExpr()))
              {
                SQLExpr_ptr owner = (std::dynamic_pointer_cast<SQLPropertyExpr>(allColumnSelectItem->getExpr()))->getOwner();
                if (instanceof <SQLExpr, SQLName>(owner))
                {
                  allColumnTableSource = from->findTableSource((std::dynamic_pointer_cast<SQLName>(owner))->nameHashCode64());
                }
              }
            }
            else
            {
              allColumnTableSource = from;
            }

            if (allColumnTableSource == nullptr)
            {
              break;
            }

            propertyExpr = std::dynamic_pointer_cast<SQLPropertyExpr>(propertyExpr->clone());
            propertyExpr->setResolvedTableSource(allColumnTableSource);

            if (instanceof <SQLTableSource, SQLExprTableSource>(allColumnTableSource))
            {
              propertyExpr->setOwner(std::dynamic_pointer_cast<SQLExpr>((std::dynamic_pointer_cast<SQLExprTableSource>(allColumnTableSource))->getExpr()->clone()));
            }
            expr = propertyExpr;
            continue;
          }
        }
      }
      else if (instanceof <SQLTableSource, SQLExprTableSource>(resolvedTableSource))
      {
        SQLExprTableSource_ptr exprTableSource = std::dynamic_pointer_cast<SQLExprTableSource>(resolvedTableSource);
        if (exprTableSource->getSchemaObject() != nullptr)
        {
          break;
        }

        SQLTableSource_ptr redirectTableSource = nullptr;
        SQLExpr_ptr tableSourceExpr = exprTableSource->getExpr();
        if (instanceof <SQLExpr, SQLIdentifierExpr>(tableSourceExpr))
        {
          redirectTableSource = (std::dynamic_pointer_cast<SQLIdentifierExpr>(tableSourceExpr))->getResolvedTableSource();
        }
        else if (instanceof <SQLExpr, SQLPropertyExpr>(tableSourceExpr))
        {
          redirectTableSource = (std::dynamic_pointer_cast<SQLPropertyExpr>(tableSourceExpr))->getResolvedTableSource();
        }

        if (redirectTableSource == resolvedTableSource)
        {
          redirectTableSource = nullptr;
        }

        if (redirectTableSource != nullptr)
        {
          propertyExpr = std::dynamic_pointer_cast<SQLPropertyExpr>(propertyExpr->clone());
          if (instanceof <SQLTableSource, SQLExprTableSource>(redirectTableSource))
          {
            propertyExpr->setOwner(std::dynamic_pointer_cast<SQLExpr>((std::dynamic_pointer_cast<SQLExprTableSource>(redirectTableSource))->getExpr()->clone()));
          }
          propertyExpr->setResolvedTableSource(redirectTableSource);
          expr = propertyExpr;
          continue;
        }

        propertyExpr = std::dynamic_pointer_cast<SQLPropertyExpr>(propertyExpr->clone());
        propertyExpr->setOwner(tableSourceExpr);
        expr = propertyExpr;
        break;
      }
    }
    break;
  }

  return expr;
}

// @Override
bool SchemaStatVisitor::visit(SQLTruncateStatement_ptr x)
{
  setMode(x, TableStat_Mode::Delete);

  for (SQLExprTableSource_ptr tableSource : *x->getTableSources())
  {
    SQLName_ptr name = std::dynamic_pointer_cast<SQLName>(tableSource->getExpr());
    TableStat_ptr stat = getTableStat(name);
    stat->incrementDeleteCount();
  }

  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLDropViewStatement_ptr x)
{
  setMode(x, TableStat_Mode::Drop);
  return true;
}

// @Override
bool SchemaStatVisitor::visit(SQLDropTableStatement_ptr x)
{
  setMode(x, TableStat_Mode::Insert);

  for (SQLExprTableSource_ptr tableSource : *x->getTableSources())
  {
    SQLName_ptr name = std::dynamic_pointer_cast<SQLName>(tableSource->getExpr());
    TableStat_ptr stat = getTableStat(name);
    stat->incrementDropCount();
  }

  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLInsertStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  setMode(x, TableStat_Mode::Insert);

  // if (x->getTableName() instanceof SQLName_ptr) {
  string_ptr ident = (std::dynamic_pointer_cast<SQLName>(x->getTableName()))->toString();

  TableStat_ptr stat = getTableStat(x->getTableName());
  stat->incrementInsertCount();
  // }

  SQLObject_list_ptr tmp = ListTran::tran_class<SQLExpr, SQLObject>(x->getColumns());
  accept(tmp);
  accept(x->getQuery());

  return false;
}

void SchemaStatVisitor::putAliasMap(std::shared_ptr<std::map<string_ptr, string_ptr>> aliasMap, string_ptr name, string_ptr value)
{
  if (aliasMap == nullptr || name == nullptr)
  {
    return;
  }
  aliasMap->insert(std::make_pair(StringUtils::toLower(name), value));
}

void SchemaStatVisitor::accept(SQLObject_ptr x)
{
  if (x != nullptr)
  {
    x->accept(SharedObject(SchemaStatVisitor));
  }
}

void SchemaStatVisitor::accept(SQLObject_list_ptr nodes)
{
  auto it = nodes->begin();
  for (int i = 0, size = nodes->size(); i < size; ++i)
  {
    std::advance(it, i);
    accept(*it);
  }
}

bool SchemaStatVisitor::visit(SQLSelectQueryBlock_ptr x)
{
  SQLTableSource_ptr from = x->getFrom();

  setMode(x, TableStat_Mode::Select);

  bool isHiveMultiInsert = false;
  if (from == nullptr)
  {
    isHiveMultiInsert = x->getParent() != nullptr && instanceof <SQLObject, HiveInsert>(x->getParent()->getParent()) && instanceof <SQLObject, HiveMultiInsertStatement>(x->getParent()->getParent()->getParent());
    if (isHiveMultiInsert)
    {
      from = (std::dynamic_pointer_cast<HiveMultiInsertStatement>(x->getParent()->getParent()->getParent())->getFrom());
    }
  }

  if (from == nullptr)
  {
    for (SQLSelectItem_ptr selectItem : *x->getSelectList())
    {
      statExpr(selectItem->getExpr());
    }
    return false;
  }

  if (from != nullptr)
  {
    from->accept(SharedObject(SchemaStatVisitor)); // 提前执行，获得aliasMap
  }

  SQLExprTableSource_ptr into = x->getInto();
  if (into != nullptr && instanceof <SQLExpr, SQLName>(into->getExpr()))
  {
    SQLName_ptr intoExpr = std::dynamic_pointer_cast<SQLName>(into->getExpr());

    bool isParam_ = instanceof <SQLName, SQLIdentifierExpr>(intoExpr) && isParam(std::dynamic_pointer_cast<SQLIdentifierExpr>(intoExpr));

    if (!isParam_)
    {
      TableStat_ptr stat = getTableStat(intoExpr);
      if (stat != nullptr)
      {
        stat->incrementInsertCount();
      }
    }
    into->accept(SharedObject(SchemaStatVisitor));
  }

  for (SQLSelectItem_ptr selectItem : *x->getSelectList())
  {
    if (selectItem->getClass()->c_str() == SQLSelectItem::class_->c_str())
    {
      statExpr(selectItem->getExpr());
    }
    else
    {
      selectItem->accept(SharedObject(SchemaStatVisitor));
    }
  }

  SQLExpr_ptr where = x->getWhere();
  if (where != nullptr)
  {
    statExpr(where);
  }

  SQLExpr_ptr startWith = x->getStartWith();
  if (startWith != nullptr)
  {
    statExpr(startWith);
  }

  SQLExpr_ptr connectBy = x->getConnectBy();
  if (connectBy != nullptr)
  {
    statExpr(connectBy);
  }

  SQLSelectGroupByClause_ptr groupBy = x->getGroupBy();
  if (groupBy != nullptr)
  {
    for (SQLExpr_ptr expr : *groupBy->getItems())
    {
      statExpr(expr);
    }
  }

  SQLWindow_list_ptr windows = x->getWindows();
  if (windows != nullptr && windows->size() > 0)
  {
    for (SQLWindow_ptr window : *windows)
    {
      window->accept(SharedObject(SchemaStatVisitor));
    }
  }

  SQLOrderBy_ptr orderBy = x->getOrderBy();
  if (orderBy != nullptr)
  {
    this->visit(orderBy);
  }

  SQLExpr_ptr first = x->getFirst();
  if (first != nullptr)
  {
    statExpr(first);
  }

  SQLSelectOrderByItem_list_ptr distributeBy = x->getDistributeBy();
  if (distributeBy != nullptr)
  {
    for (SQLSelectOrderByItem_ptr item : *distributeBy)
    {
      statExpr(item->getExpr());
    }
  }

  SQLSelectOrderByItem_list_ptr sortBy = x->getSortBy();
  if (sortBy != nullptr)
  {
    for (SQLSelectOrderByItem_ptr orderByItem : *sortBy)
    {
      statExpr(orderByItem->getExpr());
    }
  }

  for (SQLExpr_ptr expr : *x->getForUpdateOf())
  {
    statExpr(expr);
  }

  return false;
}

bool SchemaStatVisitor::isParam(SQLIdentifierExpr_ptr x)
{
  if (x->getResolvedParameter() != nullptr || x->getResolvedDeclareItem() != nullptr)
  {
    return true;
  }
  return false;
}

void SchemaStatVisitor::endVisit(SQLSelectQueryBlock_ptr x)
{
  setModeOrigin(x);
}

bool SchemaStatVisitor::visit(SQLJoinTableSource_ptr x)
{
  SQLTableSource_ptr left = x->getLeft(), right = x->getRight();

  left->accept(SharedObject(SchemaStatVisitor));
  right->accept(SharedObject(SchemaStatVisitor));

  SQLExpr_ptr condition = x->getCondition();
  if (condition != nullptr)
  {
    condition->accept(SharedObject(SchemaStatVisitor));
  }

  if (x->getUsing()->size() > 0 && instanceof <SQLTableSource, SQLExprTableSource>(left) && instanceof <SQLTableSource, SQLExprTableSource>(right))
  {
    SQLExpr_ptr leftExpr = (std::dynamic_pointer_cast<SQLExprTableSource>(left))->getExpr();
    SQLExpr_ptr rightExpr = (std::dynamic_pointer_cast<SQLExprTableSource>(right))->getExpr();

    for (SQLExpr_ptr expr : *x->getUsing())
    {
      if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
      {
        string_ptr name = (std::dynamic_pointer_cast<SQLIdentifierExpr>(expr))->getName();
        SQLPropertyExpr_ptr leftPropExpr = SQLPropertyExpr_ptr(new SQLPropertyExpr(leftExpr, name));
        SQLPropertyExpr_ptr rightPropExpr = SQLPropertyExpr_ptr(new SQLPropertyExpr(rightExpr, name));

        leftPropExpr->setResolvedTableSource(left);
        rightPropExpr->setResolvedTableSource(right);

        SQLBinaryOpExpr_ptr usingCondition = SQLBinaryOpExpr_ptr(new SQLBinaryOpExpr(leftPropExpr, SQLBinaryOperator_ptr(new SQLBinaryOperator(&SQLBinaryOperator::Equality)), rightPropExpr));
        usingCondition->accept(SharedObject(SchemaStatVisitor));
      }
    }
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLPropertyExpr_ptr x)
{
  TableStat_Column_ptr column = nullptr;
  string_ptr ident = SQLUtils::normalize(x->getName());

  SQLTableSource_ptr tableSource = x->getResolvedTableSource();

  if (instanceof <SQLTableSource, SQLSubqueryTableSource>(tableSource))
  {
    SQLSelect_ptr subSelect = (std::dynamic_pointer_cast<SQLSubqueryTableSource>(tableSource))->getSelect();
    SQLSelectQueryBlock_ptr subQuery = subSelect->getQueryBlock();
    if (subQuery != nullptr)
    {
      SQLTableSource_ptr subTableSource = subQuery->findTableSourceWithColumn(x->nameHashCode64());
      if (subTableSource != nullptr)
      {
        tableSource = subTableSource;
      }
    }
  }

  if (instanceof <SQLTableSource, SQLExprTableSource>(tableSource))
  {
    SQLExpr_ptr expr = (std::dynamic_pointer_cast<SQLExprTableSource>(tableSource))->getExpr();

    if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
    {
      SQLIdentifierExpr_ptr table = std::dynamic_pointer_cast<SQLIdentifierExpr>(expr);
      SQLTableSource_ptr resolvedTableSource = table->getResolvedTableSource();
      if (instanceof <SQLTableSource, SQLExprTableSource>(resolvedTableSource))
      {
        expr = (std::dynamic_pointer_cast<SQLExprTableSource>(resolvedTableSource))->getExpr();
      }
    }
    else if (instanceof <SQLExpr, SQLPropertyExpr>(expr))
    {
      SQLPropertyExpr_ptr table = std::dynamic_pointer_cast<SQLPropertyExpr>(expr);
      SQLTableSource_ptr resolvedTableSource = table->getResolvedTableSource();
      if (instanceof <SQLTableSource, SQLExprTableSource>(resolvedTableSource))
      {
        expr = (std::dynamic_pointer_cast<SQLExprTableSource>(resolvedTableSource))->getExpr();
      }
    }

    if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
    {
      SQLIdentifierExpr_ptr table = std::dynamic_pointer_cast<SQLIdentifierExpr>(expr);

      SQLTableSource_ptr resolvedTableSource = table->getResolvedTableSource();
      if (instanceof <SQLTableSource, SQLWithSubqueryClause_Entry>(resolvedTableSource))
      {
        return false;
      }

      string_ptr tableName = table->getName();
      SchemaObject_ptr schemaObject = (std::dynamic_pointer_cast<SQLExprTableSource>(tableSource))->getSchemaObject();
      if (schemaObject != nullptr && instanceof <SQLStatement, SQLCreateTableStatement>(schemaObject->getStatement()) && "*" != ident->c_str())
      {
        SQLColumnDefinition_ptr columnDef = schemaObject->findColumn(x->nameHashCode64());
        if (columnDef == nullptr)
        {
          column = addColumn(make_string_ptr("UNKNOWN"), ident);
        }
      }

      if (column == nullptr)
      {
        column = addColumn(table, ident);
      }

      if (isParentGroupBy(x))
      {
        this->groupByColumns->insert(column);
      }
    }
    else if (instanceof <SQLExpr, SQLPropertyExpr>(expr))
    {
      SQLPropertyExpr_ptr table = std::dynamic_pointer_cast<SQLPropertyExpr>(expr);
      column = addColumn(table, ident);

      if (column != nullptr && isParentGroupBy(x))
      {
        this->groupByColumns->insert(column);
      }
    }
    else if (instanceof <SQLExpr, SQLMethodInvokeExpr>(expr))
    {
      SQLMethodInvokeExpr_ptr methodInvokeExpr = std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr);
      if (StringUtils::equalsIgnoreCase(make_string_ptr("table"), methodInvokeExpr->getMethodName()) && methodInvokeExpr->getArguments()->size() == 1 && instanceof <SQLExpr, SQLName>(*(methodInvokeExpr->getArguments()->begin())))
      {
        SQLName_ptr table = std::dynamic_pointer_cast<SQLName>(*(methodInvokeExpr->getArguments()->begin()));

        string_ptr tableName = nullptr;
        if (instanceof <SQLName, SQLPropertyExpr>(table))
        {
          SQLPropertyExpr_ptr propertyExpr = std::dynamic_pointer_cast<SQLPropertyExpr>(table);
          SQLIdentifierExpr_ptr owner = std::dynamic_pointer_cast<SQLIdentifierExpr>(propertyExpr->getOwner());
          if (propertyExpr->getResolvedTableSource() != nullptr && instanceof <SQLTableSource, SQLExprTableSource>(propertyExpr->getResolvedTableSource()))
          {
            SQLExpr_ptr resolveExpr = (std::dynamic_pointer_cast<SQLExprTableSource>(propertyExpr->getResolvedTableSource()))->getExpr();
            if (instanceof <SQLExpr, SQLName>(resolveExpr))
            {
              std::string tmp = resolveExpr->toString()->c_str();
              tmp += ".";
              tmp += propertyExpr->getName()->c_str();
              tableName = make_string_ptr(tmp);
            }
          }
        }

        if (tableName == nullptr)
        {
          tableName = table->toString();
        }

        column = addColumn(tableName, ident);
      }
    }
  }
  else if (instanceof <SQLTableSource, SQLWithSubqueryClause_Entry>(tableSource) ||
           instanceof <SQLTableSource, SQLSubqueryTableSource>(tableSource) ||
           instanceof <SQLTableSource, SQLUnionQueryTableSource>(tableSource) ||
           instanceof <SQLTableSource, SQLValuesTableSource>(tableSource) ||
           instanceof <SQLTableSource, SQLLateralViewTableSource>(tableSource))
  {
    return false;
  }
  else
  {
    if (x->getResolvedProcudure() != nullptr)
    {
      return false;
    }

    if (instanceof <SQLObject, SQLParameter>(x->getResolvedOwnerObject()))
    {
      return false;
    }

    bool skip = false;
    for (SQLObject_ptr parent = x->getParent(); parent != nullptr; parent = parent->getParent())
    {
      if (instanceof <SQLObject, SQLSelectQueryBlock>(parent))
      {
        SQLTableSource_ptr from = (std::dynamic_pointer_cast<SQLSelectQueryBlock>(parent))->getFrom();

        if (instanceof <SQLTableSource, SQLValuesTableSource>(from))
        {
          skip = true;
          break;
        }
      }
      else if (instanceof <SQLObject, SQLSelectQuery>(parent))
      {
        break;
      }
    }
    if (!skip)
    {
      column = handleUnknownColumn(ident);
    }
  }

  if (column != nullptr)
  {
    SQLObject_ptr parent = x->getParent();
    if (instanceof <SQLObject, SQLSelectOrderByItem>(parent))
    {
      parent = parent->getParent();
      if (instanceof <SQLObject, SQLIndexDefinition>(parent))
      {
        parent = parent->getParent();
      }
    }
    if (instanceof <SQLObject, SQLPrimaryKey>(parent))
    {
      column->setPrimaryKey(true);
    }
    else if (instanceof <SQLObject, SQLUnique>(parent))
    {
      column->setUnique(true);
    }

    setColumn(x, column);
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLIdentifierExpr_ptr x)
{
  if (isParam(x))
  {
    return false;
  }

  SQLTableSource_ptr tableSource = x->getResolvedTableSource();
  if (instanceof <SQLObject, SQLSelectOrderByItem>(x->getParent()))
  {
    SQLSelectOrderByItem_ptr selectOrderByItem = std::dynamic_pointer_cast<SQLSelectOrderByItem>(x->getParent());
    if (selectOrderByItem->getResolvedSelectItem() != nullptr)
    {
      return false;
    }
  }

  if (tableSource == nullptr && (x->getResolvedParameter() != nullptr || x->getResolvedDeclareItem() != nullptr))
  {
    return false;
  }

  long hash = x->nameHashCode64();
  if (isPseudoColumn(hash))
  {
    return false;
  }

  if ((hash == FnvHash::Constants::LEVEL || hash == FnvHash::Constants::CONNECT_BY_ISCYCLE || hash == FnvHash::Constants::ROWNUM) && x->getResolvedColumn() == nullptr && tableSource == nullptr)
  {
    return false;
  }

  TableStat_Column_ptr column = nullptr;
  string_ptr ident = x->normalizedName();

  if (instanceof <SQLTableSource, SQLExprTableSource>(tableSource))
  {
    SQLExpr_ptr expr = (std::dynamic_pointer_cast<SQLExprTableSource>(tableSource))->getExpr();

    if (instanceof <SQLExpr, SQLMethodInvokeExpr>(expr))
    {
      SQLMethodInvokeExpr_ptr func = std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr);
      if (func->methodNameHashCode64() == FnvHash::Constants::ANN)
      {
        expr = *(func->getArguments()->begin());
      }
    }

    if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
    {
      SQLIdentifierExpr_ptr table = std::dynamic_pointer_cast<SQLIdentifierExpr>(expr);
      column = addColumn(table, ident);

      if (column != nullptr && isParentGroupBy(x))
      {
        this->groupByColumns->insert(column);
      }
    }
    else if (instanceof <SQLExpr, SQLPropertyExpr>(expr) ||
             instanceof <SQLExpr, SQLDbLinkExpr>(expr))
    {
      string_ptr tableName;
      if (instanceof <SQLExpr, SQLPropertyExpr>(expr))
      {
        tableName = (std::dynamic_pointer_cast<SQLPropertyExpr>(expr))->normalizedName();
      }
      else
      {
        tableName = expr->toString();
      }

      column = addColumn(tableName, ident);

      if (column != nullptr && isParentGroupBy(x))
      {
        this->groupByColumns->insert(column);
      }
    }
    else if (instanceof <SQLExpr, SQLMethodInvokeExpr>(expr))
    {
      SQLMethodInvokeExpr_ptr methodInvokeExpr = std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr);
      if (StringUtils::equalsIgnoreCase(make_string_ptr("table"), methodInvokeExpr->getMethodName()) && methodInvokeExpr->getArguments()->size() == 1 && instanceof <SQLExpr, SQLName>(*(methodInvokeExpr->getArguments()->begin())))
      {
        SQLName_ptr table = std::dynamic_pointer_cast<SQLName>(*(methodInvokeExpr->getArguments()->begin()));

        string_ptr tableName = nullptr;
        if (instanceof <SQLName, SQLPropertyExpr>(table))
        {
          SQLPropertyExpr_ptr propertyExpr = std::dynamic_pointer_cast<SQLPropertyExpr>(table);
          SQLIdentifierExpr_ptr owner = std::dynamic_pointer_cast<SQLIdentifierExpr>(propertyExpr->getOwner());
          if (propertyExpr->getResolvedTableSource() != nullptr && instanceof <SQLTableSource, SQLExprTableSource>(propertyExpr->getResolvedTableSource()))
          {
            SQLExpr_ptr resolveExpr = (std::dynamic_pointer_cast<SQLExprTableSource>(propertyExpr->getResolvedTableSource()))->getExpr();
            if (instanceof <SQLExpr, SQLName>(resolveExpr))
            {
              std::string tmp = resolveExpr->toString()->c_str();
              tmp += ".";
              tmp += propertyExpr->getName()->c_str();
              tableName = make_string_ptr(tmp);
            }
          }
        }

        if (tableName == nullptr)
        {
          tableName = table->toString();
        }

        column = addColumn(tableName, ident);
      }
    }
  }
  else if (instanceof <SQLTableSource, SQLWithSubqueryClause_Entry>(tableSource) || instanceof <SQLTableSource, SQLSubqueryTableSource>(tableSource) || instanceof <SQLTableSource, SQLLateralViewTableSource>(tableSource))
  {
    return false;
  }
  else
  {
    bool skip = false;
    for (SQLObject_ptr parent = x->getParent(); parent != nullptr; parent = parent->getParent())
    {
      if (instanceof <SQLObject, SQLSelectQueryBlock>(parent))
      {
        SQLTableSource_ptr from = (std::dynamic_pointer_cast<SQLSelectQueryBlock>(parent))->getFrom();

        if (instanceof <SQLTableSource, SQLValuesTableSource>(from))
        {
          skip = true;
          break;
        }
      }
      else if (instanceof <SQLObject, SQLSelectQuery>(parent))
      {
        break;
      }
    }
    if (instanceof <SQLObject, SQLMethodInvokeExpr>(x->getParent()) && (std::dynamic_pointer_cast<SQLMethodInvokeExpr>(x->getParent()))->methodNameHashCode64() == FnvHash::Constants::ANN)
    {
      skip = true;
    }

    if (!skip)
    {
      column = handleUnknownColumn(ident);
    }
  }

  if (column != nullptr)
  {
    SQLObject_ptr parent = x->getParent();
    if (instanceof <SQLObject, SQLSelectOrderByItem>(parent))
    {
      parent = parent->getParent();
      if (instanceof <SQLObject, SQLIndexDefinition>(parent))
      {
        parent = parent->getParent();
      }
    }
    if (instanceof <SQLObject, SQLPrimaryKey>(parent))
    {
      column->setPrimaryKey(true);
    }
    else if (instanceof <SQLObject, SQLUnique>(parent))
    {
      column->setUnique(true);
    }

    setColumn(x, column);
  }

  return false;
}

bool SchemaStatVisitor::isParentSelectItem(SQLObject_ptr parent)
{
  for (int i = 0; parent != nullptr; parent = parent->getParent(), ++i)
  {
    if (i > 100)
    {
      break;
    }

    if (instanceof <SQLObject, SQLSelectItem>(parent))
    {
      return true;
    }

    if (instanceof <SQLObject, SQLSelectQueryBlock>(parent))
    {
      return false;
    }
  }
  return false;
}

bool SchemaStatVisitor::isParentGroupBy(SQLObject_ptr parent)
{
  for (; parent != nullptr; parent = parent->getParent())
  {
    if (instanceof <SQLObject, SQLSelectItem>(parent))
    {
      return false;
    }

    if (instanceof <SQLObject, SQLSelectGroupByClause>(parent))
    {
      return true;
    }
  }
  return false;
}

void SchemaStatVisitor::setColumn(SQLExpr_ptr x, TableStat_Column_ptr column)
{
  SQLObject_ptr current = x;
  for (int i = 0; i < 100; ++i)
  {
    SQLObject_ptr parent = current->getParent();

    if (parent == nullptr)
    {
      break;
    }

    if (instanceof <SQLObject, SQLSelectQueryBlock>(parent))
    {
      SQLSelectQueryBlock_ptr query = std::dynamic_pointer_cast<SQLSelectQueryBlock>(parent);
      if (query->getWhere() == current)
      {
        column->setWhere(true);
      }
      break;
    }

    if (instanceof <SQLObject, SQLSelectGroupByClause>(parent))
    {
      SQLSelectGroupByClause_ptr groupBy = std::dynamic_pointer_cast<SQLSelectGroupByClause>(parent);
      if (current == groupBy->getHaving())
      {
        column->setHaving(true);
      }
      else if (ListUtils::contains(groupBy->getItems(), std::dynamic_pointer_cast<SQLExpr>(current)))
      {
        column->setGroupBy(true);
      }
      break;
    }

    if (isParentSelectItem(parent))
    {
      column->setSelec(true);
      break;
    }

    if (instanceof <SQLObject, SQLJoinTableSource>(parent))
    {
      SQLJoinTableSource_ptr join = std::dynamic_pointer_cast<SQLJoinTableSource>(parent);
      if (join->getCondition() == current)
      {
        column->setJoin(true);
      }
      break;
    }

    current = parent;
  }
}

TableStat_Column_ptr SchemaStatVisitor::handleUnknownColumn(string_ptr columnName)
{
  return addColumn(make_string_ptr("UNKNOWN"), columnName);
}

bool SchemaStatVisitor::visit(SQLAllColumnExpr_ptr x)
{
  SQLTableSource_ptr tableSource = x->getResolvedTableSource();
  if (tableSource == nullptr)
  {
    return false;
  }

  statAllColumn(x, tableSource);

  return false;
}

void SchemaStatVisitor::statAllColumn(SQLAllColumnExpr_ptr x, SQLTableSource_ptr tableSource)
{
  if (instanceof <SQLTableSource, SQLExprTableSource>(tableSource))
  {
    statAllColumn(x, std::dynamic_pointer_cast<SQLExprTableSource>(tableSource));
    return;
  }

  if (instanceof <SQLTableSource, SQLJoinTableSource>(tableSource))
  {
    SQLJoinTableSource_ptr join = std::dynamic_pointer_cast<SQLJoinTableSource>(tableSource);
    statAllColumn(x, join->getLeft());
    statAllColumn(x, join->getRight());
  }
}

void SchemaStatVisitor::statAllColumn(SQLAllColumnExpr_ptr x, SQLExprTableSource_ptr tableSource)
{
  SQLExprTableSource_ptr exprTableSource = tableSource;
  SQLName_ptr expr = exprTableSource->getName();

  SQLCreateTableStatement_ptr createStmt = nullptr;

  SchemaObject_ptr tableObject = exprTableSource->getSchemaObject();
  if (tableObject != nullptr)
  {
    SQLStatement_ptr stmt = tableObject->getStatement();
    if (instanceof <SQLStatement, SQLCreateTableStatement>(stmt))
    {
      createStmt = std::dynamic_pointer_cast<SQLCreateTableStatement>(stmt);
    }
  }

  if (createStmt != nullptr && createStmt->getTableElementList()->size() > 0)
  {
    SQLName_ptr tableName = createStmt->getName();
    for (SQLTableElement_ptr e : *createStmt->getTableElementList())
    {
      if (instanceof <SQLTableElement, SQLColumnDefinition>(e))
      {
        SQLColumnDefinition_ptr columnDefinition = std::dynamic_pointer_cast<SQLColumnDefinition>(e);
        SQLName_ptr columnName = columnDefinition->getName();
        TableStat_Column_ptr column = addColumn(tableName, columnName->toString());
        if (isParentSelectItem(x->getParent()))
        {
          column->setSelec(true);
        }
      }
    }
  }
  else if (expr != nullptr)
  {
    TableStat_Column_ptr column = addColumn(expr, make_string_ptr("*"));
    if (isParentSelectItem(x->getParent()))
    {
      column->setSelec(true);
    }
  }
}

bool SchemaStatVisitor::containsTable(string_ptr tableName)
{
  // return tableStats->count(TableStat_Name(tableName)) > 0;
  std::string tmp = tableName->c_str();
  return tableStats->count(tmp) > 0;
}

bool SchemaStatVisitor::containsColumn(string_ptr tableName, string_ptr columnName)
{
  long hashCode;

  int p = tableName->find('.');
  if (p != tableName->npos)
  {
    SQLExpr_ptr owner = SQLUtils::toSQLExpr(tableName, dbType);
    hashCode = (new SQLPropertyExpr(owner, columnName))->hashCode64();
  }
  else
  {
    hashCode = FnvHash::hashCode64(tableName, columnName);
  }
  return columns.count(hashCode) > 0;
}

TableStat_Column_list_ptr SchemaStatVisitor::getColumns()
{
  TableStat_Column_list_ptr out = std::make_shared<std::list<TableStat_Column_ptr>>();
  for (auto it : columns)
  {
    out->push_back(it.second);
  }
  return out;
}

TableStat_Column_ptr SchemaStatVisitor::getColumn(string_ptr tableName, string_ptr columnName)
{
  TableStat_Column_ptr column = TableStat_Column_ptr(new TableStat_Column(tableName, columnName));

  auto it = this->columns.find(column->hashCode64());
  if (it == this->columns.end())
  {
    return nullptr;
  }
  return it->second;
}

bool SchemaStatVisitor::visit(SQLSelectStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  visit(x->getSelect());

  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLWithSubqueryClause_Entry_ptr x)
{
  string_ptr alias = x->getAlias();
  SQLWithSubqueryClause_ptr with = std::dynamic_pointer_cast<SQLWithSubqueryClause>(x->getParent());

  SQLStatement_ptr returningStatement = x->getReturningStatement();
  SQLExpr_ptr expr = x->getExpr();

  if (BOOL::TRUE == with->getRecursive())
  {
    SQLSelect_ptr select = x->getSubQuery();
    if (select != nullptr)
    {
      select->accept(SharedObject(SchemaStatVisitor));
    }
    else
    {
      returningStatement->accept(SharedObject(SchemaStatVisitor));
    }
  }
  else
  {
    SQLSelect_ptr select = x->getSubQuery();
    if (select != nullptr)
    {
      select->accept(SharedObject(SchemaStatVisitor));
    }
    else if (expr != nullptr)
    {
      expr->accept(SharedObject(SchemaStatVisitor));
    }
    else
    {
      if (returningStatement != nullptr)
      {
        returningStatement->accept(SharedObject(SchemaStatVisitor));
      }
    }
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLSubqueryTableSource_ptr x)
{
  x->getSelect()->accept(SharedObject(SchemaStatVisitor));
  return false;
}

bool SchemaStatVisitor::isSimpleExprTableSource(SQLExprTableSource_ptr x)
{
  return instanceof <SQLExpr, SQLName>(x->getExpr());
}

TableStat_ptr SchemaStatVisitor::getTableStat(SQLExprTableSource_ptr tableSource)
{
  return getTableStatWithUnwrap(tableSource->getExpr());
}

TableStat_ptr SchemaStatVisitor::getTableStatWithUnwrap(SQLExpr_ptr expr)
{
  SQLExpr_ptr identExpr = nullptr;

  expr = unwrapExpr(expr);

  if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
  {
    SQLIdentifierExpr_ptr identifierExpr = std::dynamic_pointer_cast<SQLIdentifierExpr>(expr);

    if (identifierExpr->nameHashCode64() == FnvHash::Constants::DUAL)
    {
      return nullptr;
    }

    if (isSubQueryOrParamOrVariant(identifierExpr))
    {
      return nullptr;
    }
  }

  SQLTableSource_ptr tableSource = nullptr;
  if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
  {
    tableSource = (std::dynamic_pointer_cast<SQLIdentifierExpr>(expr))->getResolvedTableSource();
  }
  else if (instanceof <SQLExpr, SQLPropertyExpr>(expr))
  {
    tableSource = (std::dynamic_pointer_cast<SQLPropertyExpr>(expr))->getResolvedTableSource();
  }

  if (instanceof <SQLTableSource, SQLExprTableSource>(tableSource))
  {
    SQLExpr_ptr tableSourceExpr = (std::dynamic_pointer_cast<SQLExprTableSource>(tableSource))->getExpr();
    if (instanceof <SQLExpr, SQLName>(tableSourceExpr))
    {
      identExpr = tableSourceExpr;
    }
  }

  if (identExpr == nullptr)
  {
    identExpr = expr;
  }

  if (instanceof <SQLExpr, SQLName>(identExpr))
  {
    return getTableStat(std::dynamic_pointer_cast<SQLName>(identExpr));
  }
  return getTableStat(identExpr->toString());
}

bool SchemaStatVisitor::visit(SQLExprTableSource_ptr x)
{
  SQLExpr_ptr expr = x->getExpr();
  if (instanceof <SQLExpr, SQLMethodInvokeExpr>(expr))
  {
    SQLMethodInvokeExpr_ptr func = std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr);
    if (func->methodNameHashCode64() == FnvHash::Constants::ANN)
    {
      expr = *(func->getArguments()->begin());
    }
  }

  if (isSimpleExprTableSource(x))
  {
    TableStat_ptr stat = getTableStatWithUnwrap(expr);
    if (stat == nullptr)
    {
      return false;
    }

    TableStat_Mode_ptr mode = getMode();
    if (mode != nullptr)
    {
      if (*mode == TableStat_Mode::Delete)
      {
        stat->incrementDeleteCount();
      }
      else if (*mode == TableStat_Mode::Insert)
      {
        stat->incrementInsertCount();
      }
      else if (*mode == TableStat_Mode::Update)
      {
        stat->incrementUpdateCount();
      }
      else if (*mode == TableStat_Mode::Select)
      {
        stat->incrementSelectCount();
      }
      else if (*mode == TableStat_Mode::Merge)
      {
        stat->incrementMergeCount();
      }
      else if (*mode == TableStat_Mode::Drop)
      {
        stat->incrementDropCount();
      }
    }
  }
  else
  {
    accept(expr);
  }

  return false;
}

bool SchemaStatVisitor::isSubQueryOrParamOrVariant(SQLIdentifierExpr_ptr identifierExpr)
{
  SQLObject_ptr resolvedColumnObject = identifierExpr->getResolvedColumnObject();
  if (instanceof <SQLObject, SQLWithSubqueryClause_Entry>(resolvedColumnObject) ||
      instanceof <SQLObject, SQLParameter>(resolvedColumnObject) ||
      instanceof <SQLObject, SQLDeclareItem>(resolvedColumnObject))
  {
    return true;
  }

  SQLObject_ptr resolvedOwnerObject = identifierExpr->getResolvedOwnerObject();
  if (instanceof <SQLObject, SQLSubqueryTableSource>(resolvedOwnerObject) ||
      instanceof <SQLObject, SQLWithSubqueryClause_Entry>(resolvedOwnerObject))
  {
    return true;
  }

  return false;
}

bool SchemaStatVisitor::isSubQueryOrParamOrVariant(SQLPropertyExpr_ptr x)
{
  SQLObject_ptr resolvedOwnerObject = x->getResolvedOwnerObject();
  if (instanceof <SQLObject, SQLSubqueryTableSource>(resolvedOwnerObject) ||
      instanceof <SQLObject, SQLWithSubqueryClause_Entry>(resolvedOwnerObject))
  {
    return true;
  }

  SQLExpr_ptr owner = x->getOwner();
  if (instanceof <SQLExpr, SQLIdentifierExpr>(owner))
  {
    if (isSubQueryOrParamOrVariant(std::dynamic_pointer_cast<SQLIdentifierExpr>(owner)))
    {
      return true;
    }
  }

  SQLTableSource_ptr tableSource = x->getResolvedTableSource();
  if (instanceof <SQLTableSource, SQLExprTableSource>(tableSource))
  {
    SQLExprTableSource_ptr exprTableSource = std::dynamic_pointer_cast<SQLExprTableSource>(tableSource);
    if (exprTableSource->getSchemaObject() != nullptr)
    {
      return false;
    }

    SQLExpr_ptr expr = exprTableSource->getExpr();

    if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
    {
      return isSubQueryOrParamOrVariant(std::dynamic_pointer_cast<SQLIdentifierExpr>(expr));
    }

    if (instanceof <SQLExpr, SQLPropertyExpr>(expr))
    {
      return isSubQueryOrParamOrVariant(std::dynamic_pointer_cast<SQLPropertyExpr>(expr));
    }
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLSelectItem_ptr x)
{
  statExpr(
      x->getExpr());

  return false;
}

bool SchemaStatVisitor::visit(SQLSelect_ptr x)
{
  SQLWithSubqueryClause_ptr with = x->getWithSubQuery();
  if (with != nullptr)
  {
    with->accept(SharedObject(SchemaStatVisitor));
  }

  SQLSelectQuery_ptr query = x->getQuery();
  if (query != nullptr)
  {
    query->accept(SharedObject(SchemaStatVisitor));
  }

  SQLOrderBy_ptr orderBy = x->getOrderBy();
  if (orderBy != nullptr)
  {
    accept(x->getOrderBy());
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLAggregateExpr_ptr x)
{
  this->aggregateFunctions->push_back(x);

  SQLObject_list_ptr tmp = ListTran::tran_class<SQLExpr, SQLObject>(x->getArguments());
  accept(tmp);
  accept(x->getOrderBy());
  accept(x->getOver());
  return false;
}

bool SchemaStatVisitor::visit(SQLMethodInvokeExpr_ptr x)
{
  this->functions->push_back(x);

  SQLObject_list_ptr tmp = ListTran::tran_class<SQLExpr, SQLObject>(x->getArguments());
  accept(tmp);
  return false;
}

bool SchemaStatVisitor::visit(SQLUpdateStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  setMode(x, TableStat_Mode::Update);

  SQLTableSource_ptr tableSource = x->getTableSource();
  if (instanceof <SQLTableSource, SQLExprTableSource>(tableSource))
  {
    SQLName_ptr identName = (std::dynamic_pointer_cast<SQLExprTableSource>(tableSource))->getName();
    TableStat_ptr stat = getTableStat(identName);
    stat->incrementUpdateCount();
  }
  else
  {
    tableSource->accept(SharedObject(SchemaStatVisitor));
  }

  SQLTableSource_ptr from = x->getFrom();
  if (from != nullptr)
  {
    from->accept(SharedObject(SchemaStatVisitor));
  }

  SQLUpdateSetItem_list_ptr items = x->getItems();
  auto it = items->begin();
  for (int i = 0, size = items->size(); i < size; ++i)
  {
    std::advance(it, i);
    SQLUpdateSetItem_ptr item = *it;
    visit(item);
  }

  SQLExpr_ptr where = x->getWhere();
  if (where != nullptr)
  {
    where->accept(SharedObject(SchemaStatVisitor));
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLUpdateSetItem_ptr x)
{
  SQLExpr_ptr column = x->getColumn();
  if (column != nullptr)
  {
    statExpr(column);

    TableStat_Column_ptr columnStat = getColumn(column);
    if (columnStat != nullptr)
    {
      columnStat->setUpdate(true);
    }
  }

  SQLExpr_ptr value = x->getValue();
  if (value != nullptr)
  {
    statExpr(value);
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLDeleteStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  setMode(x, TableStat_Mode::Delete);

  if (instanceof <SQLTableSource, SQLSubqueryTableSource>(x->getTableSource()))
  {
    SQLSelectQuery_ptr selectQuery = (std::dynamic_pointer_cast<SQLSubqueryTableSource>(x->getTableSource()))->getSelect()->getQuery();
    if (instanceof <SQLSelectQuery, SQLSelectQueryBlock>(selectQuery))
    {
      SQLSelectQueryBlock_ptr subQueryBlock = (std::dynamic_pointer_cast<SQLSelectQueryBlock>(selectQuery));
      subQueryBlock->getWhere()->accept(SharedObject(SchemaStatVisitor));
    }
  }

  TableStat_ptr stat = getTableStat(x->getTableName());
  stat->incrementDeleteCount();

  SQLExpr_ptr where = x->getWhere();
  if (where != nullptr)
  {
    where->accept(SharedObject(SchemaStatVisitor));
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLInListExpr_ptr x)
{
  if (x->isNot())
  {
    handleCondition(x->getExpr(), make_string_ptr("NOT IN"), x->getTargetList());
  }
  else
  {
    handleCondition(x->getExpr(), make_string_ptr("IN"), x->getTargetList());
  }

  return true;
}

// @Override
bool SchemaStatVisitor::visit(SQLInSubQueryExpr_ptr x)
{
  if (x->isNot())
  {
    handleCondition(x->getExpr(), make_string_ptr("NOT IN"));
  }
  else
  {
    handleCondition(x->getExpr(), make_string_ptr("IN"));
  }
  return true;
}

void SchemaStatVisitor::endVisit(SQLDeleteStatement_ptr x)
{
}

void SchemaStatVisitor::endVisit(SQLUpdateStatement_ptr x)
{
}

bool SchemaStatVisitor::visit(SQLCreateTableStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  for (SQLTableElement_ptr e : *x->getTableElementList())
  {
    e->setParent(x);
  }

  TableStat_ptr stat = getTableStat(x->getName());
  stat->incrementCreateCount();

  SQLObject_list_ptr tmp = ListTran::tran_class<SQLTableElement, SQLObject>(x->getTableElementList());
  accept(tmp);

  if (x->getInherits() != nullptr)
  {
    x->getInherits()->accept(SharedObject(SchemaStatVisitor));
  }

  if (x->getSelect() != nullptr)
  {
    x->getSelect()->accept(SharedObject(SchemaStatVisitor));
    stat->incrementInsertCount();
  }

  SQLExprTableSource_ptr like = x->getLike();
  if (like != nullptr)
  {
    like->accept(SharedObject(SchemaStatVisitor));
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLColumnDefinition_ptr x)
{
  SQLName_ptr tableName = nullptr;
  {
    SQLObject_ptr parent = x->getParent();
    if (instanceof <SQLObject, SQLCreateTableStatement>(parent))
    {
      tableName = (std::dynamic_pointer_cast<SQLCreateTableStatement>(parent))->getName();
    }
  }

  if (tableName == nullptr)
  {
    return true;
  }

  string_ptr columnName = x->getName()->toString();
  TableStat_Column_ptr column = addColumn(tableName, columnName);
  if (x->getDataType() != nullptr)
  {
    column->setDataType(x->getDataType()->getName());
  }

  for (SQLColumnConstraint_ptr item : *x->getConstraints())
  {
    if (instanceof <SQLColumnConstraint, SQLPrimaryKey>(item))
    {
      column->setPrimaryKey(true);
    }
    else if (instanceof <SQLColumnConstraint, SQLUnique>(item))
    {
      column->setUnique(true);
    }
  }

  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLAlterTableAddColumn_ptr x)
{
  SQLAlterTableStatement_ptr stmt = std::dynamic_pointer_cast<SQLAlterTableStatement>(x->getParent());
  string_ptr table = stmt->getName()->toString();

  for (SQLColumnDefinition_ptr column : *x->getColumns())
  {
    string_ptr columnName = SQLUtils::normalize(column->getName()->toString());
    addColumn(stmt->getName(), columnName);
  }
  return false;
}

bool SchemaStatVisitor::visit(SQLCreateViewStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  SQLSelect_ptr subQuery = x->getSubQuery();
  if (subQuery != nullptr)
  {
    subQuery->accept(SharedObject(SchemaStatVisitor));
  }

  SQLBlockStatement_ptr script = x->getScript();
  if (script != nullptr)
  {
    script->accept(SharedObject(SchemaStatVisitor));
  }
  return false;
}

bool SchemaStatVisitor::visit(SQLAlterViewStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  x->getSubQuery()->accept(SharedObject(SchemaStatVisitor));
  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLAlterTableStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  TableStat_ptr stat = getTableStat(x->getName());
  stat->incrementAlterCount();

  for (SQLAlterTableItem_ptr item : *x->getItems())
  {
    item->setParent(x);
    if (instanceof <SQLAlterTableItem, SQLAlterTableAddPartition>(item) ||
        instanceof <SQLAlterTableItem, SQLAlterTableRenamePartition>(item) ||
        instanceof <SQLAlterTableItem, SQLAlterTableMergePartition>(item))
    {
      stat->incrementAddPartitionCount();
    }

    item->accept(SharedObject(SchemaStatVisitor));
  }

  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLDropIndexStatement_ptr x)
{
  setMode(x, TableStat_Mode::DropIndex);
  SQLExprTableSource_ptr table = x->getTableName();
  if (table != nullptr)
  {
    SQLName_ptr name = std::dynamic_pointer_cast<SQLName>(table->getExpr());
    TableStat_ptr stat = getTableStat(name);
    stat->incrementDropIndexCount();
  }
  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLCreateIndexStatement_ptr x)
{
  setMode(x, TableStat_Mode::CreateIndex);

  SQLName_ptr table = std::dynamic_pointer_cast<SQLName>((std::dynamic_pointer_cast<SQLExprTableSource>(x->getTable()))->getExpr());
  TableStat_ptr stat = getTableStat(table);
  stat->incrementCreateIndexCount();

  for (SQLSelectOrderByItem_ptr item : *x->getItems())
  {
    SQLExpr_ptr expr = item->getExpr();
    if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
    {
      SQLIdentifierExpr_ptr identExpr = std::dynamic_pointer_cast<SQLIdentifierExpr>(expr);
      string_ptr columnName = identExpr->getName();
      addColumn(table, columnName);
    }
    else if (instanceof <SQLExpr, SQLMethodInvokeExpr>(expr))
    {
      SQLMethodInvokeExpr_ptr methodInvokeExpr = std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr);
      if (methodInvokeExpr->getArguments()->size() == 1)
      {
        SQLExpr_ptr param = *(methodInvokeExpr->getArguments()->begin());
        if (instanceof <SQLExpr, SQLIdentifierExpr>(param))
        {
          SQLIdentifierExpr_ptr identExpr = std::dynamic_pointer_cast<SQLIdentifierExpr>(param);
          string_ptr columnName = identExpr->getName();
          addColumn(table, columnName);
        }
      }
    }
  }

  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLForeignKeyImpl_ptr x)
{
  for (SQLName_ptr column : *x->getReferencingColumns())
  {
    column->accept(SharedObject(SchemaStatVisitor));
  }

  SQLName_ptr table = x->getReferencedTableName();

  TableStat_ptr stat = getTableStat(x->getReferencedTableName());
  stat->incrementReferencedCount();
  for (SQLName_ptr column : *x->getReferencedColumns())
  {
    string_ptr columnName = column->getSimpleName();
    addColumn(table, columnName);
  }

  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLGrantStatement_ptr x)
{
  if (x->getResource() != nullptr &&
      (x->getResourceType() == nullptr ||
       x->getResourceType()->name->c_str() == SQLObjectType::TABLE.name->c_str()))
  {
    x->getResource()->accept(SharedObject(SchemaStatVisitor));
  }
  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLRevokeStatement_ptr x)
{
  if (x->getResource() != nullptr)
  {
    x->getResource()->accept(SharedObject(SchemaStatVisitor));
  }
  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLAlterTableAddIndex_ptr x)
{
  for (SQLSelectOrderByItem_ptr item : *x->getColumns())
  {
    item->accept(SharedObject(SchemaStatVisitor));
  }

  SQLName_ptr table = (std::dynamic_pointer_cast<SQLAlterTableStatement>(x->getParent()))->getName();
  TableStat_ptr tableStat = this->getTableStat(table);
  tableStat->incrementCreateIndexCount();
  return false;
}

bool SchemaStatVisitor::visit(SQLCheck_ptr x)
{
  x->getExpr()->accept(SharedObject(SchemaStatVisitor));
  return false;
}

bool SchemaStatVisitor::visit(SQLDefault_ptr x)
{
  x->getExpr()->accept(SharedObject(SchemaStatVisitor));
  x->getColumn()->accept(SharedObject(SchemaStatVisitor));
  return false;
}

bool SchemaStatVisitor::visit(SQLCreateTriggerStatement_ptr x)
{
  SQLExprTableSource_ptr on = x->getOn();
  on->accept(SharedObject(SchemaStatVisitor));
  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLArrayExpr_ptr x)
{
  SQLObject_list_ptr tmp = ListTran::tran_class<SQLExpr, SQLObject>(x->getValues());
  accept(tmp);

  SQLExpr_ptr exp = x->getExpr();
  if (instanceof <SQLExpr, SQLIdentifierExpr>(exp))
  {
    if ((std::dynamic_pointer_cast<SQLIdentifierExpr>(exp))->getName()->c_str() == "ARRAY")
    {
      return false;
    }
  }
  if (exp != nullptr)
  {
    exp->accept(SharedObject(SchemaStatVisitor));
  }
  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLCreateProcedureStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  accept(x->getBlock());
  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLCreateFunctionStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  accept(x->getBlock());
  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLBlockStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  for (SQLParameter_ptr param : *x->getParameters())
  {
    param->setParent(x);
    param->accept(SharedObject(SchemaStatVisitor));
  }

  for (SQLStatement_ptr stmt : *x->getStatementList())
  {
    stmt->accept(SharedObject(SchemaStatVisitor));
  }

  SQLStatement_ptr exception = x->getException();
  if (exception != nullptr)
  {
    exception->accept(SharedObject(SchemaStatVisitor));
  }

  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLMergeStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  setMode(x->getUsing(), TableStat_Mode::Select);
  x->getUsing()->accept(SharedObject(SchemaStatVisitor));

  setMode(x, TableStat_Mode::Merge);

  SQLTableSource_ptr into = x->getInto();
  if (instanceof <SQLTableSource, SQLExprTableSource>(into))
  {
    string_ptr ident = (std::dynamic_pointer_cast<SQLExprTableSource>(into))->getExpr()->toString();
    TableStat_ptr stat = getTableStat(ident);
    stat->incrementMergeCount();
  }
  else
  {
    into->accept(SharedObject(SchemaStatVisitor));
  }

  x->getOn()->accept(SharedObject(SchemaStatVisitor));

  if (x->getUpdateClause() != nullptr)
  {
    x->getUpdateClause()->accept(SharedObject(SchemaStatVisitor));
  }

  if (x->getInsertClause() != nullptr)
  {
    x->getInsertClause()->accept(SharedObject(SchemaStatVisitor));
  }

  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLAlterTableAddConstraint_ptr x)
{
  SQLConstraint_ptr constraint = x->getConstraint();
  if (instanceof <SQLConstraint, SQLUniqueConstraint>(constraint))
  {
    SQLAlterTableStatement_ptr stmt = std::dynamic_pointer_cast<SQLAlterTableStatement>(x->getParent());
    TableStat_ptr tableStat = this->getTableStat(stmt->getName());
    tableStat->incrementCreateIndexCount();
  }
  return true;
}

// @Override
bool SchemaStatVisitor::visit(SQLAlterTableDropIndex_ptr x)
{
  SQLAlterTableStatement_ptr stmt = std::dynamic_pointer_cast<SQLAlterTableStatement>(x->getParent());
  TableStat_ptr tableStat = this->getTableStat(stmt->getName());
  tableStat->incrementDropIndexCount();
  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLAlterTableDropPrimaryKey_ptr x)
{
  SQLAlterTableStatement_ptr stmt = std::dynamic_pointer_cast<SQLAlterTableStatement>(x->getParent());
  TableStat_ptr tableStat = this->getTableStat(stmt->getName());
  tableStat->incrementDropIndexCount();
  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLAlterTableDropKey_ptr x)
{
  SQLAlterTableStatement_ptr stmt = std::dynamic_pointer_cast<SQLAlterTableStatement>(x->getParent());
  TableStat_ptr tableStat = this->getTableStat(stmt->getName());
  tableStat->incrementDropIndexCount();
  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLDescribeStatement_ptr x)
{
  SQLName_ptr tableName = x->getObject();

  TableStat_ptr tableStat = this->getTableStat(x->getObject());

  SQLName_ptr column = x->getColumn();
  if (column != nullptr)
  {
    string_ptr columnName = column->toString();
    this->addColumn(tableName, columnName);
  }
  return false;
}

bool SchemaStatVisitor::visit(SQLExplainStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  if (x->getStatement() != nullptr)
  {
    accept(x->getStatement());
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLCreateMaterializedViewStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  SQLSelect_ptr query = x->getQuery();
  if (query != nullptr)
  {
    query->accept(SharedObject(SchemaStatVisitor));
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLReplaceStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  setMode(x, TableStat_Mode::Replace);

  SQLName_ptr tableName = x->getTableName();

  TableStat_ptr stat = getTableStat(tableName);

  if (stat != nullptr)
  {
    stat->incrementInsertCount();
  }

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(x->getColumns());
  accept(tmp1);
  SQLObject_list_ptr tmp2 = ListTran::tran_class<SQLInsertStatement_ValuesClause, SQLObject>(x->getValuesList());
  accept(tmp2);
  accept(x->getQuery());

  return false;
}

void SchemaStatVisitor::statExpr(SQLExpr_ptr x)
{
  if (x == nullptr)
  {
    return;
  }

  string_ptr clazz = x->getClass();
  if (clazz->c_str() == "SQLIdentifierExpr")
  {
    visit(std::dynamic_pointer_cast<SQLIdentifierExpr>(x));
  }
  else if (clazz->c_str() == "SQLPropertyExpr")
  {
    visit(std::dynamic_pointer_cast<SQLPropertyExpr>(x));
    //        } else if (clazz == SQLAggregateExpr.class) {
    //            visit((SQLAggregateExpr)_ptr x);
  }
  else if (clazz->c_str() == "SQLBinaryOpExpr")
  {
    visit(std::dynamic_pointer_cast<SQLBinaryOpExpr>(x));
    //        } else if (clazz == SQLCharExpr.class) {
    //            visit((SQLCharExpr)_ptr x);
    //        } else if (clazz == SQLNullExpr.class) {
    //            visit((SQLNullExpr)_ptr x);
    //        } else if (clazz == SQLIntegerExpr.class) {
    //            visit((SQLIntegerExpr)_ptr x);
    //        } else if (clazz == SQLNumberExpr.class) {
    //            visit((SQLNumberExpr)_ptr x);
    //        } else if (clazz == SQLMethodInvokeExpr.class) {
    //            visit((SQLMethodInvokeExpr)_ptr x);
    //        } else if (clazz == SQLVariantRefExpr.class) {
    //            visit((SQLVariantRefExpr)_ptr x);
    //        } else if (clazz == SQLBinaryOpExprGroup.class) {
    //            visit((SQLBinaryOpExprGroup)_ptr x);
  }
  else if (instanceof <SQLExpr, SQLLiteralExpr>(x))
  {
    // skip
  }
  else
  {
    x->accept(SharedObject(SchemaStatVisitor));
  }
}

bool SchemaStatVisitor::visit(SQLExprStatement_ptr x)
{
  SQLExpr_ptr expr = x->getExpr();

  if (instanceof <SQLExpr, SQLName>(expr))
  {
    return false;
  }

  return true;
}

bool SchemaStatVisitor::visit(SQLShowColumnsStatement_ptr x)
{
  SQLName_ptr table = x->getTable();

  TableStat_ptr stat = getTableStat(table);
  if (stat != nullptr)
  {
    //            stat.incrementSh
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLShowCreateTableStatement_ptr x)
{
  SQLName_ptr table = x->getName();

  if (table != nullptr)
  {
    TableStat_ptr stat = getTableStat(table);
    if (stat != nullptr)
    {
      //            stat.incrementSh
    }
  }

  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLAlterTableExchangePartition_ptr x)
{
  SQLExprTableSource_ptr table = x->getTable();
  if (table != nullptr)
  {
    table->accept(SharedObject(SchemaStatVisitor));
  }
  return false;
}

bool SchemaStatVisitor::visit(SQLUnionQuery_ptr x)
{
  SQLUnionOperator_ptr operator_ = x->getOperator();
  SQLSelectQuery_list_ptr relations = x->getRelations();
  if (relations->size() > 2)
  {
    for (SQLSelectQuery_ptr relation : *x->getRelations())
    {
      relation->accept(SharedObject(SchemaStatVisitor));
    }
    return false;
  }

  SQLSelectQuery_ptr left = x->getLeft();
  SQLSelectQuery_ptr right = x->getRight();

  bool bracket = x->isParenthesized() && !(instanceof <SQLObject, SQLUnionQueryTableSource>(x->getParent()));

  if ((!bracket) && instanceof <SQLSelectQuery, SQLUnionQuery>(left) && (std::dynamic_pointer_cast<SQLUnionQuery>(left))->getOperator() == operator_ && !right->isParenthesized() && x->getOrderBy() == nullptr)
  {
    SQLUnionQuery_ptr leftUnion = std::dynamic_pointer_cast<SQLUnionQuery>(left);

    SQLSelectQuery_list_ptr rights = std::make_shared<std::list<SQLSelectQuery_ptr>>();
    rights->push_back(right);

    if (leftUnion->getRelations()->size() > 2)
    {
      // rights.addAll(leftUnion->getRelations());
      for (auto it : *leftUnion->getRelations())
      {
        rights->push_back(it);
      }
    }
    else
    {
      for (;;)
      {
        SQLSelectQuery_ptr leftLeft = leftUnion->getLeft();
        SQLSelectQuery_ptr leftRight = leftUnion->getRight();

        if ((!leftUnion->isParenthesized()) && leftUnion->getOrderBy() == nullptr && (!leftLeft->isParenthesized()) && (!leftRight->isParenthesized()) && instanceof <SQLSelectQuery, SQLUnionQuery>(leftLeft) && (std::dynamic_pointer_cast<SQLUnionQuery>(leftLeft))->getOperator()->name->c_str() == operator_->name->c_str())
        {
          rights->push_back(leftRight);
          leftUnion = std::dynamic_pointer_cast<SQLUnionQuery>(leftLeft);
          continue;
        }
        else
        {
          rights->push_back(leftRight);
          rights->push_back(leftLeft);
        }
        break;
      }
    }

    auto it_rights = rights->begin();
    for (int i = rights->size() - 1; i >= 0; i--)
    {
      std::advance(it_rights, i);
      SQLSelectQuery_ptr item = *it_rights;
      item->accept(SharedObject(SchemaStatVisitor));
    }
    return false;
  }

  return true;
}

bool SchemaStatVisitor::visit(SQLAlterIndexStatement_ptr x)
{
  SQLExprTableSource_ptr table = x->getTable();
  if (table != nullptr)
  {
    table->accept(SharedObject(SchemaStatVisitor));
  }
  return false;
}

bool SchemaStatVisitor::visit(SQLShowIndexesStatement_ptr x)
{
  SQLExprTableSource_ptr table = x->getTable();
  if (table != nullptr)
  {
    table->accept(SharedObject(SchemaStatVisitor));
  }
  return false;
}

bool SchemaStatVisitor::visit(SQLAnalyzeTableStatement_ptr x)
{
  for (SQLExprTableSource_ptr table : *x->getTables())
  {
    if (table != nullptr)
    {
      TableStat_ptr stat = getTableStat(table->getName());
      if (stat != nullptr)
      {
        stat->incrementAnalyzeCount();
      }
    }
  }

  SQLExprTableSource_ptr table = x->getTables()->size() == 1 ? (*(x->getTables()->begin())) : nullptr;

  SQLPartitionRef_ptr partition = x->getPartition();
  if (partition != nullptr)
  {
    for (SQLPartitionRef_Item_ptr item : *partition->getItems())
    {
      SQLIdentifierExpr_ptr columnName = item->getColumnName();
      columnName->setResolvedTableSource(table);
      columnName->accept(SharedObject(SchemaStatVisitor));
    }
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLExportTableStatement_ptr x)
{
  SQLExprTableSource_ptr table = x->getTable();
  if (table != nullptr)
  {
    table->accept(SharedObject(SchemaStatVisitor));
  }

  for (SQLAssignItem_ptr item : *x->getPartition())
  {
    SQLExpr_ptr target = item->getTarget();
    if (instanceof <SQLExpr, SQLIdentifierExpr>(target))
    {
      (std::dynamic_pointer_cast<SQLIdentifierExpr>(target))->setResolvedTableSource(table);
      target->accept(SharedObject(SchemaStatVisitor));
    }
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLImportTableStatement_ptr x)
{
  SQLExprTableSource_ptr table = x->getTable();
  if (table != nullptr)
  {
    table->accept(SharedObject(SchemaStatVisitor));
  }

  for (SQLAssignItem_ptr item : *x->getPartition())
  {
    SQLExpr_ptr target = item->getTarget();
    if (instanceof <SQLExpr, SQLIdentifierExpr>(target))
    {
      (std::dynamic_pointer_cast<SQLIdentifierExpr>(target))->setResolvedTableSource(table);
      target->accept(SharedObject(SchemaStatVisitor));
    }
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLCreateOutlineStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  if (x->getOn() != nullptr)
  {
    x->getOn()->accept(SharedObject(SchemaStatVisitor));
  }

  if (x->getTo() != nullptr)
  {
    x->getTo()->accept(SharedObject(SchemaStatVisitor));
  }

  return false;
}

bool SchemaStatVisitor::visit(SQLDumpStatement_ptr x)
{
  if (repository != nullptr && x->getParent() == nullptr)
  {
    repository->resolve(x);
  }

  SQLExprTableSource_ptr into = x->getInto();
  if (into != nullptr)
  {
    into->accept(SharedObject(SchemaStatVisitor));
  }

  SQLSelect_ptr select = x->getSelect();
  if (select != nullptr)
  {
    select->accept(SharedObject(SchemaStatVisitor));
  }

  return false;
}

// @Override

// @Override
bool SchemaStatVisitor::visit(SQLCopyFromStatement_ptr x)
{
  SQLExprTableSource_ptr table = x->getTable();
  if (table != nullptr)
  {
    table->accept(SharedObject(SchemaStatVisitor));
    for (SQLName_ptr column : *x->getColumns())
    {
      addColumn(table->getName(), column->getSimpleName());
    }
  }

  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLCloneTableStatement_ptr x)
{
  SQLExprTableSource_ptr from = x->getFrom();
  if (from != nullptr)
  {
    TableStat_ptr stat = getTableStat(from->getName());
    if (stat != nullptr)
    {
      stat->incrementSelectCount();
    }
  }

  SQLExprTableSource_ptr to = x->getTo();
  if (to != nullptr)
  {
    TableStat_ptr stat = getTableStat(to->getName());
    if (stat != nullptr)
    {
      stat->incrementInsertCount();
    }
  }
  return false;
}

// @Override
bool SchemaStatVisitor::visit(SQLUnique_ptr x)
{
  for (SQLSelectOrderByItem_ptr column : *x->getColumns())
  {
    column->accept(SharedObject(SchemaStatVisitor));
  }
  return false;
}

bool SchemaStatVisitor::visit(SQLShowPartitionsStmt_ptr x)
{
  setMode(x, TableStat_Mode::DESC);
  return true;
}
